This reference covers the cross-cutting utility functions that tie Arsinous V8 Sales together: theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/arsinousltd-sudo/Arsinous-V8-Sales/llms.txt
Use this file to discover all available pages before exploring further.
onOpen and onEdit spreadsheet triggers, the sidebar and user-property helpers, the generic tab router updateCurrentTab, date-range logic used by every query-driven refresh, the All Balances and Balance tab writers, inventory sync, and the two custom Sheets formulas for running balance and accounts receivable aging. These functions live across Updates.gs, Formulas.gs, SoA.gs, onOpen.gs, onEdit.gs, and sidebar/backend.gs.
onOpen(e)
Trigger — runs automatically when the spreadsheet is opened.
Creates the Arsinous custom menu in the spreadsheet UI and, when the script has full auth, auto-opens the sidebar.
| Label | Function called |
|---|---|
| Sidebar | showSidebar |
| Update Current Tab | updateCurrentTab |
| (separator) | — |
| GET Discouts | getDiscounts |
| SAVE Discouts | saveDiscounts |
The menu item labels “GET Discouts” and “SAVE Discouts” contain a typo in the source (
Discouts instead of Discounts). These are the exact strings that appear in the Arsinous menu.The sidebar is opened automatically only when
e.authMode != ScriptApp.AuthMode.NONE — that is, when the script is running with at least limited authorization. In LIMITED or NONE mode (e.g., in an unbound script context), the sidebar call is skipped.onEdit(e)
Trigger — runs automatically on any cell edit.
Routes edits to the appropriate update function based on the sheet name and the edited cell’s A1 notation.
| Sheet | Cell edited | Action |
|---|---|---|
Discounts | A1 | getDiscounts() — reloads discount grid for the customer in H2 |
Balance | A1 | showTransactions() — reloads all transactions for the customer named in A1 |
P&D | C1, C2, E1, or C4 | updateCustomersPayments() — re-runs the payment query with updated filters |
showSidebar()
Opens the Arsinous sidebar panel (300 px wide) rendered from sidebar/index.html.
refreshAllProperties and read back by getDates, updateInventory, and similar functions.
updateCurrentTab()
Routes to the correct sheet-refresh function based on the name of the active sheet. This is the function bound to the Arsinous → Update Current Tab menu item.
| Sheet name | Function called |
|---|---|
Inventory | updateInventory() |
Products | updateProducts() |
Customers | updateCustomers() |
Invoices | updateInvoices() |
P&D | updateCustomersPayments() |
All Balances | updateAllBalances() |
Balance | showTransactions() |
Discounts | getDiscounts() |
getDates()
Reads time-period preferences from user properties and returns a { startDate, endDate } object with both dates formatted as "YYYY-MM-DD" strings ready for SQL WHERE clauses.
timePeriod values:
| Value | Start date | End date |
|---|---|---|
"all" | 1970-01-01 | Today |
"prevYear" | Jan 1 of last year | Dec 31 of last year |
"thisYear" | Jan 1 of this year | Today |
"prevMonth" | 1st of last month | Last day of last month |
"thisMonth" | 1st of this month | Today |
| any other | props.startDate | props.endDate |
{ startDate: "YYYY-MM-DD", endDate: "YYYY-MM-DD" } — used internally by updateInvoices.
updateAllBalances()
Clears the All Balances sheet and rewrites a per-customer summary showing each customer’s total outstanding balance (invoices minus payments).
UNION of two aggregates — payments summed as negative amounts and invoices summed as positive — grouped and totalled per customer name. A positive balance means the customer owes money; a negative balance means overpayment.
Data rows start at A3, sorted alphabetically by customer name. A timestamp is written to B1.
updateBalanceTab()
Refreshes the SoA Inputs sheet with a Statement of Account for a selected customer, including an optional opening balance for the date range.
Filter inputs are read from the SoA Inputs sheet:
| Cell | Variable | Description |
|---|---|---|
C1 | startDate | Start of period (display value) |
C2 | endDate | End of period (display value) |
F1 | customerId | Numeric customer ID |
startDate is set, a separate query sums all invoices and payments before the start date and writes the result to cell B5. This becomes the “brought forward” figure.
Main query: A UNION ALL of invoices and payments for the customer within the date range, ordered by date. Columns written are date, type, name/num, amount (invoices positive, payments negative). Results write from row 11.
If
customerId is empty or non-numeric, updateBalanceTab writes "Customer have to be selected" into cell A11 and skips the database query entirely.updateInventory(hideEmpty, sortCol)
Refreshes the Inventory sheet by merging product, lot (irs_items), and purchase order (pos_items) data into a single denormalised view.
When
true, products with no in-stock lots and no open purchase orders are excluded from the output. Defaults to the hideEmpty user property (set via the sidebar), or false if not set.The zero-based column index to sort by.
1 = id (numeric sort), 2 = name (alpha sort). Defaults to the orderCol user property, or 2 (name) if not set.| Column | Content |
|---|---|
| A | Checkbox (data validation applied to A3:A) |
| B | Product id |
| C | Product name |
| D | Category |
| E | Total in-stock quantity (sum across all lots) |
| F | Total on-order quantity (sum across open POs) |
| G | Lot identifiers, expiry dates, and per-lot quantities (newline-separated) |
| H | Lot costs (newline-separated) |
| I | Lot final costs (newline-separated) |
"Inventory" + updated timestamp). Row 2 is the column header row. Data starts at row 3.
Returns: JSON.stringify({ message: "Updated." }) — used by the sidebar to confirm completion.
showTransactions()
Fetches all invoices and payments for the customer named in cell A1 of the Balance sheet, merges them into a single chronological list, and writes the result starting at row 3.
Id, Date, Type, Name, Amount, VAT, Total, Comment.
Invoice rows: [id, date, type, name, total, vat, total+vat, comments]
Payment rows: [id, date, type, num, "", "", -amount, comments] — amount is negated to represent money out.
The function resolves the customer record by matching A1’s text value against customers.name. This means the name must match the database exactly, including case.
runningBallance(amounts)
A custom Sheets formula function. Takes a single-column range of numbers and returns a same-length array of cumulative running totals.
A single-column range of numeric values (as passed from a Google Sheets formula, e.g.
=runningBallance(D11:D100)). Trailing empty rows are trimmed before processing. Non-numeric values are treated as 0.accoutsReceivable(transactions, date)
A custom Sheets formula function that produces an accounts receivable aging analysis from a transaction range. Buckets the unpaid balance into five aging bands: 0–30 days, 30–60 days, 60–90 days, 90–120 days, and over 120 days.
A range of transaction data with columns: date (col 0), type (col 1), name (col 2), amount (col 3). If not provided, the function reads from
Balance!A11:D automatically and takes the reference date from Balance!C2.The reference date for aging calculations. Required when
transactions is passed explicitly; auto-read from Balance!C2 otherwise.Ctrl+Shift+Enter):
readProperties()
Returns all user properties as a JSON string. Used internally by getDates, updateInventory, and any function that reads sidebar settings.
timePeriod, keyword, orderCol, hideEmpty, startDate, endDate.
updateProperty(key, value)
Sets a single user property key to a given value. Called by the sidebar to update individual settings without overwriting the full property set.
The property key to set (e.g.,
"timePeriod", "orderCol", "hideEmpty").The value to store. All user properties are stored as strings.
refreshAllProperties(data)
Bulk-replaces the five primary sidebar-controlled user properties in one call. This is called when the user clicks “Apply” in the sidebar after adjusting multiple settings.
A JSON string with the following fields:
| Field | Description |
|---|---|
timePeriod | One of: "all", "prevYear", "thisYear", "prevMonth", "thisMonth", or "custom". |
keyword | Search/filter keyword (used by the sidebar UI). |
orderCol | Inventory sort column: 1 = by id, 2 = by name. |
startDate | Custom start date string (used when timePeriod is custom). |
endDate | Custom end date string (used when timePeriod is custom). |
editInvoiceOrNote(type)
Validates the active sheet and row, verifies the selected document matches the expected type, then opens the invoice edit dialog.
The expected document type:
"invoice" or "credit note". If the selected row’s type column does not match this value, the function throws an error.- Active sheet must be
"Invoices"or"Balance", and cell A of the selected row must be non-empty. - On the
"Invoices"sheet: row must be ≥ 2; type is read from column 2. - On the
"Balance"sheet: row must be ≥ 4; type is read from column 3. - If the row’s document type does not equal the
typeargument, throws"Selected document is <actual_type> but not <type>".
showInvoice(id, type) with the id from column A of the selected row.
deleteInvoiceOrNote(type)
Same validation as editInvoiceOrNote, then shows a confirmation dialog and — if confirmed — calls deleteInvoice(id).
The expected document type:
"invoice" or "credit note"."Are you sure you want to delete <type> with id:<id>". If the user clicks YES, deleteInvoice(id) is called. If NO, a toast "Operation canceled." is shown.
navigateTo(type, id)
Opens a URL in a new browser tab by rendering a minimal HTML redirect modal that calls window.open(...) and immediately closes itself.
"sheet" to open a Google Sheets document, or "folder" to open a Google Drive folder.The Google Drive file ID (for sheets) or folder ID (for folders) to open.
The redirect modal uses
IFRAME sandbox mode so that window.open is not blocked. The modal closes itself via google.script.host.close() immediately after opening the tab. A manual Close button is also provided for browsers that block pop-ups.