{"files":[{"id":"075980a5-5893-4063-9629-da5e55fc3c49","name":"appsscript","type":"json","source":"{\n  \"timeZone\": \"Asia/Yangon\",\n  \"dependencies\": {\n  },\n  \"exceptionLogging\": \"STACKDRIVER\",\n  \"runtimeVersion\": \"V8\"\n}"},{"id":"71385f93-a41c-4ca8-aee2-ed25259a5196","name":"Code","type":"server_js","source":"// \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d CONFIGURATION \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n// --- UPDATE THESE VALUES WITH YOUR DETAILS ---\n\n// 1. Google Drive Folder ID where the weekly sheets will be saved.\nconst FOLDER_ID \u003d \u00271rla_QZmXl6P6lV_2neApaaJY6QyF5T1o\u0027;\n\n// 2. MySQL Database Connection Details\nconst DB_HOST \u003d \u0027db-distribution-accounting-do-user-2718740-0.h.db.ondigitalocean.com\u0027;\nconst DB_PORT \u003d 25060;\nconst DB_NAME \u003d \u0027das-db\u0027;\nconst DB_USER \u003d \u0027thansoeaung\u0027;\nconst DB_PASSWORD \u003d \u0027cW8w3EQxFFWZfKBZeWjNbQ\u003d\u003d\u0027;\n\n// 3. Your Full SQL Queries and Target Sheet (Tab) Names\nconst QUERIES_TO_RUN \u003d [\n  {\n    sheetName: \u0027data\u0027,\n    query: `\n    SELECT \n    inv.order_number,\n    inv.invoice_number,\n    DATE_FORMAT(inv.created_at, \u0027%M-%y\u0027) AS InvoiceMonth,\n    inv.created_at,\n    devm.name AS DeliveryMethod,\n    SUM(sod.detail_qty) AS TotalQty,\n    -- This sums up the total weight of all items in the invoice\n    SUM(sod.detail_qty * p.weight) AS InvoiceTotalWeight\n    \nFROM sales_invoices AS inv\n    \nLEFT JOIN sales_orders AS so ON inv.sales_order_id \u003d so.id\nLEFT JOIN delivery_methods AS devm ON devm.id \u003d so.delivery_method_id\nLEFT JOIN sales_order_details AS sod ON so.id \u003d sod.sales_order_id\nLEFT JOIN products AS p ON sod.product_id \u003d p.id\n    \nWHERE inv.business_id \u003d \u002784093770-29ad-4e8e-9da1-babe583c0d69\u0027\n\n-- Grouping by the unique invoice and order fields\nGROUP BY \n    inv.invoice_number, \n    inv.order_number, \n    InvoiceMonth, \n    inv.created_at, \n    DeliveryMethod;\n    `\n  }\n];\n\n// 4. Email Notification Configuration\n\nconst SPREADSHEET_ID \u003d \"17BxuaTjxvYqDytbMU7VRceNIqVYUqo0CD_e9LgUhz_o\"; \nconst TAB_ID \u003d 0; // 0 is typically the very first tab in a sheet, so this is fine!\nconst ss \u003d SpreadsheetApp.openById(SPREADSHEET_ID);\nvar EMAIL_RECIPIENTS \u003d \"\";\n\n// Fixed: changed ss.getSheet() to ss.getSheets()\nconst email_sheet \u003d ss.getSheets().find(s \u003d\u003e s.getSheetId() \u003d\u003d\u003d TAB_ID);\n\nif (email_sheet) {\n  EMAIL_RECIPIENTS \u003d email_sheet.getRange(\"G2\").getValue();\n  Logger.log(EMAIL_RECIPIENTS);\n} else {\n  Logger.log(\"Error: Could not find a tab with ID \" + TAB_ID);\n}\n\n\n// \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d END OF CONFIGURATION \u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\u003d\n\n\n/**\n * The main function to be scheduled weekly.\n */\nfunction mainMonthlyDataPull() {\n  try {\n    // --- 1. Calculate Dates and Filename ---\n    const today \u003d new Date();\n    // To get the date of the most recent Sunday for the filename.\n    const lastMonth \u003d new Date(today.setDate(today.getDate() - today.getDay()));\n    const timezone \u003d Session.getScriptTimeZone();\n    const baseName \u003d Utilities.formatDate(lastMonth, timezone, \"[MMM] \u0027M-Kitchen --\u003e M-Express Deliveries Master Data Snapshot\u0027\");\n\n    // --- 2. Create the new Spreadsheet ---\n    const parentFolder \u003d DriveApp.getFolderById(FOLDER_ID);\n    const newSheet \u003d SpreadsheetApp.create(baseName);\n    const tempFile \u003d DriveApp.getFileById(newSheet.getId());\n    parentFolder.addFile(tempFile);\n    DriveApp.getRootFolder().removeFile(tempFile);\n    \n    Logger.log(`Successfully created new sheet: \"${baseName}\" (ID: ${newSheet.getId()})`);\n\n    // --- 3. Establish Database Connection ---\n    const dbUrl \u003d \"jdbc:mysql://\" + DB_HOST + \":\" + DB_PORT + \"/\" + DB_NAME;\n    const conn \u003d Jdbc.getConnection(dbUrl, DB_USER, DB_PASSWORD);\n    Logger.log(\"Successfully connected to the database.\");\n\n    // --- 4. Loop Through Each Query and Populate Sheets ---\n    QUERIES_TO_RUN.forEach(item \u003d\u003e {\n      Logger.log(`Fetching data for sheet: ${item.sheetName}`);\n      \n      const data \u003d fetchData(conn, item.query);\n      \n      if (data \u0026\u0026 data.length \u003e 1) { \n        Logger.log(`Retrieved ${data.length - 1} rows of data.`);\n        writeDataToSheet(newSheet, item.sheetName, data);\n      } else {\n        Logger.log(`No data found for sheet: ${item.sheetName}.`);\n        writeDataToSheet(newSheet, item.sheetName, [[\"No data found for the provided query\"]]);\n      }\n    });\n\n    // --- 5. Clean up Default Tabs ---\n    const defaultSheet \u003d newSheet.getSheetByName(\u0027Sheet1\u0027);\n    if (defaultSheet) {\n      newSheet.deleteSheet(defaultSheet);\n    }\n    conn.close();\n    Logger.log(\"Process complete. Database connection closed.\");\n\n    // --- 6. Export Target Tab to CSV and Send Email ---\n    Logger.log(\"Preparing email notification...\");\n    sendCsvEmail(newSheet, QUERIES_TO_RUN[0].sheetName, baseName);\n    \n  } catch (e) {\n    Logger.log(`An error occurred: ${e.message} \\nStack: ${e.stack}`);\n  }\n}\n\n/**\n * Fetches data from the database using a given query.\n */\nfunction fetchData(conn, query) {\n  const stmt \u003d conn.createStatement();\n  const rs \u003d stmt.executeQuery(query);\n  const numCols \u003d rs.getMetaData().getColumnCount();\n  const headers \u003d [];\n  for (let col \u003d 1; col \u003c\u003d numCols; col++) {\n    headers.push(rs.getMetaData().getColumnLabel(col));\n  }\n  \n  const data \u003d [headers];\n  \n  while (rs.next()) {\n    const row \u003d [];\n    for (let col \u003d 1; col \u003c\u003d numCols; col++) {\n      row.push(rs.getString(col));\n    }\n    data.push(row);\n  }\n  \n  rs.close();\n  stmt.close();\n  return data;\n}\n\n/**\n * Writes a 2D array of data to a specified sheet.\n */\nfunction writeDataToSheet(ss, sheetName, data) {\n  let sheet \u003d ss.getSheetByName(sheetName);\n  if (!sheet) {\n    sheet \u003d ss.insertSheet(sheetName);\n  }\n  sheet.clear();\n  \n  if (data \u0026\u0026 data.length \u003e 0) {\n    const range \u003d sheet.getRange(1, 1, data.length, data[0].length);\n    range.setValues(data);\n    sheet.setFrozenRows(1);\n    Logger.log(`Wrote data to sheet: \"${sheetName}\"`);\n  }\n}\n\n/**\n * Converts a specific sheet tab to a CSV blob and emails it to the team.\n * @param {Spreadsheet} ss The spreadsheet object.\n * @param {string} targetSheetName The specific tab name to convert.\n * @param {string} baseName The base filename used for subject and attachment.\n */\nfunction sendCsvEmail(ss, targetSheetName, baseName) {\n  const targetSheet \u003d ss.getSheetByName(targetSheetName);\n  if (!targetSheet) {\n    Logger.log(`Error: Could not find tab \"${targetSheetName}\" to export to CSV.`);\n    return;\n  }\n\n  // Get data from the sheet and convert to a CSV string format\n  const data \u003d targetSheet.getDataRange().getValues();\n  let csvString \u003d \"\";\n  \n  for (let i \u003d 0; i \u003c data.length; i++) {\n    const row \u003d data[i].map(cell \u003d\u003e {\n      // Escape inner quotes and wrap cells containing commas/newlines in quotes\n      let cellStr \u003d cell.toString();\n      if (cellStr.includes(\",\") || cellStr.includes(\"\\n\") || cellStr.includes(\u0027\"\u0027)) {\n        cellStr \u003d \u0027\"\u0027 + cellStr.replace(/\"/g, \u0027\"\"\u0027) + \u0027\"\u0027;\n      }\n      return cellStr;\n    });\n    csvString +\u003d row.join(\",\") + \"\\r\\n\";\n  }\n\n  // Convert string to an email attachment blob\n  const attachmentFilename \u003d `${baseName}.csv`;\n  const csvBlob \u003d Utilities.newBlob(csvString, \u0027text/csv\u0027, attachmentFilename);\n\n  // Draft and send email\n  const subject \u003d `Weekly Data Update: ${baseName}`;\n  const body \u003d `Hello Team,\\n\\nPlease find attached the monthly automated CSV data snapshot upto: ${baseName}.\\n\\nThis is an automated system email.`;\n\n  MailApp.sendEmail({\n    to: EMAIL_RECIPIENTS,\n    subject: subject,\n    body: body,\n    attachments: [csvBlob]\n  });\n\n  Logger.log(`Notification email successfully sent to: ${EMAIL_RECIPIENTS}`);\n}\n"}]}