Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| moneyworks:billig_shipping_script [2026/02/18 00:45] – created Martin Falconer | moneyworks:billig_shipping_script [2026/02/18 18:59] (current) – Martin Falconer | ||
|---|---|---|---|
| Line 2: | Line 2: | ||
| - | The Billig shipping script, named 'billig_shipping' in the Airtox File, is for sending shipping | + | The Billig shipping script, named 'Export_Billig' in the Airtox File, is for sending shipping |
| + | It runs as a MW script and uses CURL to send shipping orders over tcp/http. It is triggered by selecting Sales Invoices and pressing the " | ||
| ====== Technical documentation ====== | ====== Technical documentation ====== | ||
| - | ====== Key features: ====== | + | ===== Key features: ===== |
| + | |||
| + | * **MW Sales Invoice Sending:** Sends selected sales invoices to the Billig API. | ||
| + | * **Checks Sales Invoices not already processed: | ||
| + | * **Validates Delivery Addresses: | ||
| + | * **Validates Country 2 Char representation: | ||
| + | |||
| + | ===== Missing features: ===== | ||
| + | |||
| + | * Unable to resend if send failure. | ||
| + | * No local record of sent orders (with MW SQLite, this could be done. Again, script is pretty busy already) | ||
| + | * If when adding a Sales Line to Sales Header fails.... no resend available as per above. Just gets left off order (has only happened a few times) | ||
| + | * UI feedback while working. Bit of a limitation of long running MW script. (Only recommend sending max of 10 Sales Invoices) | ||
| + | |||
| + | ===== BILLIG API Information: | ||
| + | |||
| + | **API is locked down by IPs.** The available IPs are Martin' | ||
| + | API looks like it is maintained by prentow.com (Contacts not added here as public page) | ||
| + | |||
| + | ==== Test Site Enpoints ==== | ||
| + | |||
| + | * Sales Header endpoint: http:// | ||
| + | * Sales Line endpoint: http:// | ||
| + | * Header duplication check: http:// | ||
| + | |||
| + | ==== Live Site Enpoints ==== | ||
| + | |||
| + | * Sales Header endpoint: http:// | ||
| + | * Sales Line endpoint: http:// | ||
| + | * Header duplication check: http:// | ||
| + | |||
| + | Auth info is hard coded in script. | ||
| + | |||
| + | The API is what can be deduced as a Microsoft Dynamics 365 API. The API is very basic and not very ideal, but it has been rock solid for 3 years despite its shortcomings. | ||
| + | Some of those short commings are: | ||
| + | |||
| + | * Cannot immediately check for duplicated orders. | ||
| + | * Uses HTTP and not HTTPS | ||
| + | * The test site is never updated with data and rules from the " | ||
| + | * Uses antiquated NTLM for authentication. | ||
| + | |||
| + | ==== How API Works ==== | ||
| + | |||
| + | * Before adding a Sales Header, check if order already exists using the Header Check duplication endpoint | ||
| + | * A Sales Header is created using the Sales Header endpoint | ||
| + | * Individual lines are added to Sales Header using the Lines Header endpoint | ||
| + | |||
| + | |||
| + | === Sales Header Fields (text from script) === | ||
| + | |||
| + | Document_No = " | ||
| + | External_Docuement_No = " | ||
| + | shippingCodeLines = See below how shippingCodeLines are calculated\\ | ||
| + | |||
| + | let pdata = " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + shippingCodeLines | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + "\n} | ||
| + | |||
| + | === Sales Line Fields (text from script) === | ||
| + | |||
| + | Document_No = " | ||
| + | Line_No = sequential integer number. 1, 2, 3, etc...\\ | ||
| + | |||
| + | let pdata = " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | let pdata = pdata + " | ||
| + | |||
| + | === Shipping Code Lines === | ||
| + | |||
| + | A few shipping codes are sent in the Sales Header. | ||
| + | * " | ||
| + | * " | ||
| - | * **MW Transaction Export:** Extracts transactions from Moneyworks, builds UBL XML documents using JSON templates, and exports them to Edimondo. | + | The logic depends (as of today, 17th Feb 2026): |
| - | * **MW Transaction Reprocessing: | + | |
| - | * **Edimondo Status Checking:** Checks the delivery status of sent documents and updates MW/DB accordingly. | + | |
| - | * **Order Import:** Retrieves orders from Edimondo, parses UBL XML, and imports them into MW. | + | |
| - | * **Configurable:** Uses TOML configuration files for server, business, and email settings. | + | |
| - | * **Logging: | + | |
| - | * **Utilities: | + | |
| - | ====== | + | * If internal order is for customer " |
| + | * " | ||
| + | * " | ||
| + | * If destination country " | ||
| + | * " | ||
| + | * " | ||
| + | * If destination country " | ||
| + | * " | ||
| + | * " | ||
| + | * If destination country is none of the above, and not " | ||
| + | * " | ||
| + | * " | ||
| - | * mw_to_ubl.rs: MW → UBL/ | + | ==== Entry point: ==== |
| - | * ubl_to_mw.rs: | + | |
| - | * mw.rs: MW API communication. | + | |
| - | * edimondo.rs: | + | |
| - | * invoice.rs, order.rs: UBL document construction and parsing. | + | |
| - | * export.rs, operations.rs: | + | |
| - | * db.rs: Database operations for exports/ | + | |
| - | * config.rs: Configuration management. | + | |
| - | * utilities.rs: | + | |
| - | ====== Entry point: ====== | + | on Before: |
| + | InstallToolBarIcon(windowRef, | ||
| + | end | ||
| + | |||
| + | ==== Script Logic and Steps ==== | ||
| - | main.rs calls `start_server` which runs the main service loop. | + | * User highlights Sales Invoices to send and presses " |
| + | * Check each Transaction is of type " | ||
| + | * Check Transaction.user_4 field is empty AND != " | ||
| + | * Check there are Transactions selected. | ||
| + | * Attempt to send transactions with following validations for each transaction | ||
| + | * Validate again if user_4 is empty or anything but " | ||
| + | * Check if Web Order. | ||
| + | * Parse transaction delivery addresses and validate | ||
| + | * Address must be 4, 5, or 6 lines | ||
| + | * If only 4 lines, third line must have City and Postcode on same line, in that order | ||
| + | * First line is always | ||
| + | * Last line is always the country name, or country 2 code letters, nothing more. i.e " | ||
| + | * Second to last line must always be JUST postocde, or have postcode as last value on line. i.e " | ||
| + | * Parse for 2 Char country code if full country name is given | ||
| + | * Check all Transaction lines are from the " | ||
| + | * Check order doesn' | ||
| + | * Create Sales Header | ||
| + | * Add lines to Sales Header, one by one | ||
| + | | ||
