RMAX Custom — User Guide

Complete guide for administrators and branch users to work with the RMAX Custom app on ERPNext.

💰

POS Sales

Cash & Credit invoices with payment popup

Branch Setup

Auto-managed permissions per branch

📦

Stock Transfers

Transfer with approval workflow

🔄

Inter-Company

Auto Purchase Invoice on submit

🖶

Landed Cost CBM

Distribute charges by cubic meters

👤

Quick Customer

Create customer directly from invoice

🏢 Branch Master Admin

The Branch master is the first record you create for a new physical / commercial branch. Every Branch Configuration, per-branch document numbering series, and per-branch letterhead is keyed off this record.

How to Create a Branch

  1. Search for Branch and click + Add Branch.
  2. Enter the Branch name (e.g. "Azzizziyah", "Warehouse Jeddah", "HQ Awtad"). This becomes the record ID.
  3. Set Doc Prefix — the short alphanumeric prefix used in this branch's document numbers. The prefix MUST end with a hyphen. Examples: WJ-, AZZ-, HO-, CL1-.
  4. Set Branch Name (Arabic) — used inside per-branch bilingual letterheads.
  5. (Optional) Set Letter Head if you want to override the auto-generated RMAX - <Branch> letterhead with a different one.
  6. Click Save.
What runs automatically on save:
  • Branch Naming Series rows are seeded for 16 doctypes (Sales Invoice, Delivery Note, Purchase Receipt, Purchase Invoice, Payment Entry, Stock Entry, Stock Reconciliation, Quotation, Material Request, Stock Transfer, Journal Entry, Sales Order, Purchase Order, Damage Slip, Damage Transfer, No VAT Sale).
  • Templates like WJ-INV-.YYYY.-.####, WJ-DN-.YYYY.-.####, WJ-PE-.YYYY.-.#### are added to each doctype's series picker.
  • Return variants are seeded for SI / DN / PI — e.g. SI → WJ-CN-.YYYY.-.#### (Credit Note), DN → WJ-DRN-.YYYY.-.####, PI → WJ-DN-.YYYY.-.#### (Debit Note).
  • RMAX - <Branch> Letter Head is created with the branch's Arabic name + bilingual address (when the branch is in the shipped data list).
  • Inter-Branch GL accountsDue from <Branch> and Due to <Branch> — are added under each root Company.
Prefix uniqueness: Each branch's Doc Prefix must be globally unique — two branches sharing a prefix will collide on the per-doctype series counter. Standard practice: 2–4 letters that map to the branch name.

⚙ Branch Configuration Admin

Branch Configuration is the per-Branch operational config — exactly one row per Branch master. It controls which Company, Warehouses, Cost Centers, Users, and Modes of Payment belong to the branch. When you save, user permissions are automatically created for every user in the list.

Prerequisite: You must create the Branch master first — including its Doc Prefix — before creating its Branch Configuration.

How to Create a Branch Configuration

  1. Go to the search bar and type Branch Configuration, then click + Add Branch Configuration.
  2. Select the Branch (e.g. "Jeddah").
  3. Select the Company this branch belongs to (e.g. "Clearlight New Co."). This is required.
  4. In the Modes of Payment table, add the Cash and Bank Modes of Payment that branch users may collect against (e.g. Cash, Mada, Bank Transfer - HSBC). Only MoPs with type Cash or Bank appear in the picker. BNPL providers (Tabby/Tamara) are NOT added here — they are common to every branch.
  5. In the Warehouse table, add the warehouses for this branch. They will be filtered to show only warehouses from the selected company. The first warehouse becomes the user's default.
  6. In the Cost Center table, add cost centers. They are also filtered by company. The first cost center becomes the default.
  7. In the User table, add all users who work in this branch (e.g. shameel@rmax.com, naseef@rmaxled.com).
  8. Click Save.
Auto Permissions: On save, the system automatically creates User Permissions for every user × Company, Branch, Warehouse, and Cost Center. Company, first Warehouse, and first Cost Center are set as the user's default, so they auto-fill in new documents like Sales Invoices.
💰
Modes of Payment behaviour: When a Branch User saves a Sales Invoice, payment rows whose Mode of Payment type is Cash or Bank are constrained to this list. If the user picks a Cash MoP not in the list, the system swaps to the first allowed Cash row and re-syncs the GL account from the Mode of Payment’s company-specific row. Same for Bank. BNPL rows are never touched. If the table is left empty, no filtering is applied (legacy behaviour).
Changing Company: If you change the Company field, the Warehouse and Cost Center tables will be cleared automatically because they belong to the old company. You'll need to re-add them.

What Happens Automatically

ActionResult
Save with usersUser Permissions created for Company, Branch, Warehouse, Cost Center
Remove a userTheir permissions are automatically deleted
Change CompanyOld company permission removed, new one created
User logs inDefault Company, Warehouse, Cost Center auto-fill in new documents
🚫
Important: Warehouses and Cost Centers must belong to the selected Company. The system validates this on save and will show an error if there's a mismatch.

🔢 Branch Naming Series Admin

Every branch gets its own document number prefix per doctype. Counters are independent per doctype per branch, so Sales Invoice AZZ-INV-2026-0001 and Payment Entry AZZ-PE-2026-0001 are unrelated — they don't share a counter.

Template Pattern

DoctypeAbbrevExample templateReturn variant
Sales InvoiceINVAZZ-INV-.YYYY.-.####AZZ-CN-.YYYY.-.#### (Credit Note)
Delivery NoteDNAZZ-DN-.YYYY.-.####AZZ-DRN-.YYYY.-.####
Purchase InvoicePIAZZ-PI-.YYYY.-.####AZZ-DN-.YYYY.-.#### (Debit Note)
Purchase ReceiptPRAZZ-PR-.YYYY.-.####
Payment EntryPEAZZ-PE-.YYYY.-.####
Stock EntrySEAZZ-SE-.YYYY.-.####
Stock ReconciliationSRAZZ-SR-.YYYY.-.####
QuotationQTAZZ-QT-.YYYY.-.####
Material RequestMRAZZ-MR-.YYYY.-.####
Stock TransferSTAZZ-ST-.YYYY.-.####
Journal EntryJVAZZ-JV-.YYYY.-.####
Sales OrderSOAZZ-SO-.YYYY.-.####
Purchase OrderPOAZZ-PO-.YYYY.-.####
Damage SlipDSAZZ-DS-.YYYY.-.####
Damage TransferDTAZZ-DT-.YYYY.-.####
No VAT SaleNVSAZZ-NVS-.YYYY.-.####
Auto-pick at document creation: When a Branch User creates a new Sales Invoice / DN / etc, the system reads their default branch (from Branch Configuration) and auto-selects the matching template. Cancel rows / Credit Notes auto-pick the return variant. Operators with System Manager / Sales Manager / Stock Manager / Sales Master Manager can override the picker manually.
Already-saved documents keep their old names. If a doc was saved before the branch's prefix was configured, its name stays as-is — only NEW docs from that point onward use the per-branch templates.

📝 Branch Letterheads Admin

Each branch ships with a bilingual (English / Arabic) letterhead containing the company name, branch address, VAT number, CR number, and website. Branch-specific data — the address block in both languages and the mobile number — comes from the branch's letterhead record.

How Letterhead Resolution Works

  1. If the document has an explicit Letter Head field set, that one is used.
  2. Otherwise, the system looks up RMAX - <Branch> matching the doc's branch (e.g. RMAX - Azzizziyah).
  3. Falls back to the RMAX - Clear Light master letterhead if neither of the above exists.
Inline render: The print format RMAX Tax Invoice ZATCA embeds the letterhead inline at the top of the page. Operator's "No Letter Head" toggle in the print dialog cannot strip it — the header always renders.

Editing a Branch Letterhead

  1. Search Letter Head → open RMAX - <Branch>.
  2. Set Source to HTML (not Image).
  3. Edit the HTML body. Standard placeholders like {{ company.company_name }}, {{ company.tax_id }}, {{ co_address.address_line1 }} are supported via Jinja.
  4. Save.
Don't disable the letterhead. Setting Disabled = 1 makes the system fall through to the next candidate (branch → master). To customise, edit the HTML body in place rather than disabling.

👥 Branch User Role Admin

The Branch User role is automatically assigned when a user is added to a Branch Configuration. It grants permissions for daily branch operations.

Automatic Role Assignment

ActionResult
Add user to Branch Configuration"Branch User" role auto-assigned
Remove user from ALL Branch Configurations"Branch User" role auto-removed

Permission Matrix

DocTypeReadWriteCreateSubmitCancel
Sales InvoiceYesYesYesYesYes
QuotationYesYesYesYesNo
CustomerYesYesYes--
Payment EntryYesYesYesYesNo
Purchase ReceiptYesYesYesYesYes
Delivery NoteYesYesYesYesNo
Purchase InvoiceYesYesYesYesNo
Stock TransferYesYesYesYesNo
Material RequestYesYesYesYesNo
ItemYesNoNo--
Price ListYesNoNo--
Stock EntryYesNoNo--
Inter Company BranchYesNoNo--
No VAT SaleNo access — Sales Manager only (changed Apr 2026).
🔒
Item Restrictions: Branch Users can view items but cannot see cost/valuation fields (valuation rate, last purchase rate, standard rate). Inter-company price lists are also hidden.

Three User Roles

RolePurpose
Branch UserSales staff at branches. Full access to sales/purchase documents. Restricted on stock/cost data.
Warehouse UserStock keepers. Can view MRs, create stock transfers, view stock reports. (Default ERPNext role)
Stock ManagerFull stock control. Can approve transfers, see all data. (Default ERPNext role)

🔄 Inter Company Branch Admin

Used when you sell from one company to another (internal customer). This configuration tells the system which Cost Center and Warehouse to use when auto-creating the Purchase Invoice in the buying company.

Setup Steps

  1. Go to Inter Company Branch list and click + Add.
  2. Enter a Branch Name (e.g. "Jeddah Branch").
  3. In the Company Cost Centers table, add a row for each company involved:
    • Select the Company (the buying company)
    • Select the Cost Center for that company
    • Select the Warehouse for that company
  4. Save.
💡
How it works: When a Sales Invoice with an internal customer is submitted, the system automatically creates a draft Purchase Invoice in the buying company. It uses the Cost Center and Warehouse from this Inter Company Branch configuration. The Inter Company Branch field on the Sales Invoice determines which branch config to use.

Auto-Created Purchase Invoice Details

FieldSource
Supplier Invoice NoSales Invoice name
Supplier Invoice DateSales Invoice posting date
Cost CenterInter Company Branch → Company row
WarehouseInter Company Branch → Company row
TaxesDefault Purchase Taxes template for the buying company

🔃 Workflows Admin

The Stock Transfer document uses an approval workflow.

Stock Transfer Workflow

Draft
Pending
Approved
Rejected

StateWho Can ActWhat Happens
DraftAny userUser creates and fills in the transfer details
PendingAny userTransfer submitted for approval
ApprovedApprover roleStock Entry (Material Transfer) is auto-created and submitted
RejectedApprover roleTransfer is rejected, no stock movement

💰 Sales Invoice — Cash Mode

The most common daily operation. Create a sales invoice with cash payment.

Step-by-Step

  1. Open a New Sales Invoice. The Company, Warehouse, and Cost Center will auto-fill from your branch defaults.
  2. Set Payment Mode to Cash.
  3. Select or create a Customer. (You can use the "Create New Customer" button to quickly add one.)
  4. Add items to the Items table. Use Enter to move between columns and rows.
    The system will check stock automatically and warn you if quantity exceeds available stock.
  5. Click Save. A Payment Popup will appear.
  6. In the popup, allocate payment amounts across different payment modes (Cash, Card, etc.):
    • Click a payment mode button to fill the full amount to that mode
    • Or click the input field to auto-fill the remaining balance
    • You can split payments across multiple modes
  7. Click Save & Submit to submit the invoice and create Payment Entries automatically.
💡
Quick Tip: Use the New Invoice button (top-right) to open a new Sales Invoice in a new browser tab while the current one is still open.
💰
Payment Entries: When you click "Save & Submit", the system automatically creates one Payment Entry per payment mode and submits them. You don't need to create Payment Entries manually.

💳 Sales Invoice — Credit Mode

For credit sales where the customer pays later.

Step-by-Step

  1. Create a New Sales Invoice as usual.
  2. Set Payment Mode to Credit.
  3. Notice that the Customer dropdown is now filtered to show only customers with a credit limit.
  4. Select a customer and add items.
  5. Click Save. A confirmation dialog will ask: "Do you want to Submit this Sales Invoice now?"
    • Click Yes to submit immediately
    • Click No to save as draft
Note: In Credit mode, there is no payment popup. Payment will be collected later through a separate Payment Entry.

💸 Tabby / Tamara BNPL — Overview

RMAX accepts Tabby and Tamara as BNPL (Buy Now Pay Later) payment methods. Both providers deduct an 8% commission when they settle to RMAX's bank, so the selling price is automatically uplifted by 8.6957% (÷ 0.92) on the post-discount rate. The customer pays the uplifted amount via Tabby/Tamara; the provider deducts 8% and remits the original intended amount to RMAX. The uplift is applied only to the BNPL-funded portion of the invoice — cash or card splits are not affected.

📊
Formula: Effective rate = Post-discount rate × (1 + BNPL portion ratio × surcharge%). For a fully Tabby-paid invoice with surcharge 8.6957%, the line rate becomes base_rate / 0.92.
Scope: Applies to Sales Invoice only. POS Invoice, e-commerce, and any non-Sales-Invoice channel are out of scope. Cost of Goods Sold (COGS) and Stock Ledger valuation are not affected by the uplift.

How money flows

  1. Customer buys goods worth SAR 100 (post-discount). Invoice is raised at SAR 108.70 with Tabby as Mode of Payment.
  2. Tabby pays the customer's instalments and holds the SAR 108.70 in a clearing account.
  3. 1–2 days later Tabby remits SAR 100.00 net to RMAX's bank, after deducting their 8% commission (SAR 8.70).
  4. Accounts team opens a new Journal Entry (Bank Entry) and clicks Load BNPL Settlement — dialog asks for Mode of Payment + Bank Account, pre-fills three rows (Dr Bank, Dr BNPL Fee Expense, Cr Tabby Clearing). Operator types the actual amounts from the provider statement and submits.
  5. Net P&L impact: gross profit on the original SAR 100 sale is preserved — the surcharge collected exactly offsets the provider's commission.

⚙ BNPL Setup Admin

One-time configuration per Company. The Chart of Account heads listed below are auto-created by the app on the next bench migrate. The accountant only needs to wire them to the right Mode of Payment / Company records.

Step 1 — Account heads (auto-created per Company)

The app provisions these accounts the first time it migrates after install. Replace <abbr> with the Company abbreviation (e.g. R for "RMAX").

Account Name Parent Group Account Type Root Type Purpose
Tabby Clearing - <abbr> Bank Accounts Bank Asset Holds gross Tabby receivable between SI submit and settlement.
Tamara Clearing - <abbr> Bank Accounts Bank Asset Holds gross Tamara receivable between SI submit and settlement.
BNPL Fee Expense - <abbr> Indirect Expenses Expense Account Expense Records provider commission (Tabby/Tamara fees) on settlement.
Important — Account Type must be "Bank", not "Receivable". If a previous setup created Tabby - <abbr> as a Receivable account, ERPNext will refuse to auto-post the Payment Entry on Sales Invoice submit ("Customer is required against Receivable account..."). Switch to a Bank-type clearing account using the auto-created Tabby Clearing - <abbr>.

Step 2 — Configure Mode of Payment (Tabby, Tamara)

For each BNPL provider:

  1. Go to Accounting → Mode of Payment. Open Tabby (create the record if it does not exist; type = General).
  2. Set Surcharge Percentage = 8.6957.
  3. Set BNPL Clearing Account = Tabby Clearing - <abbr>.
  4. In the standard Accounts child table at the bottom, add a row per Company: Default Account = the same Tabby Clearing - <abbr>. This is what ERPNext uses when auto-creating the Payment Entry on SI submit.
  5. Save.
  6. Repeat for Tamara using Tamara Clearing - <abbr> and the same Surcharge Percentage.
🔒
The Surcharge Percentage field is editable only by users with the Accounts Manager role — same access level as Mode of Payment itself.

Step 3 — Verify Company default fee account

The app sets Company.custom_bnpl_fee_account automatically the first time it provisions accounts. To check or change it:

  1. Open Accounting → Company → <your company>.
  2. Scroll to the section containing BNPL Fee Expense Account.
  3. Confirm it points to BNPL Fee Expense - <abbr>. Change it if your CoA uses a different head.
  4. Save.

Step 4 — Smoke test

Create a small test Sales Invoice (1 item, SAR 100, Customer = a real customer with credit limit). Use Cash mode first to confirm normal POS flow works. Then create a fresh test invoice with Tabby and verify:

💰 BNPL on Sales Invoice

Day-to-day invoice flow for cashiers and sales users. The uplift is fully automatic — no manual rate adjustment is required.

Full BNPL payment (100% Tabby or Tamara)

  1. Create a New Sales Invoice as usual. Set Payment Mode = Cash (the existing RMAX category, kept separate from BNPL).
  2. Add the customer and the items. Apply any line discount or invoice discount first; the uplift always operates on the post-discount rate.
  3. In the POS-style Enter Payment Amounts popup, set the full invoice total against Tabby (or Tamara). Cash row should be 0.00.
  4. The line rates jump up by ÷ 0.92 in real time. The BNPL Portion Ratio shows 100%.
  5. Click Save & Submit.

Split payment (cash + BNPL)

Example: SAR 1,000 invoice paid SAR 400 cash + SAR 600 Tabby.

  1. Add Cash row = SAR 400. Add Tabby row = SAR 600.
  2. BNPL Portion Ratio = 60%. Effective uplift factor = 1 + (0.60 × 0.086957) = 1.05217.
  3. Each line rate is uplifted by 5.217% — the cash portion is not uplifted, only the Tabby-funded share. The customer-facing invoice shows a single uplifted total.
🔄
Switching from Tabby to Cash mid-edit: If you remove the Tabby payment row and replace with Cash, the rates revert to their original pre-uplift values automatically. The Original Rate and BNPL Uplift Amount columns are cleared.

Returns / Credit Notes

When you return a Tabby-paid invoice through the standard Create → Return / Credit Note action, the credit note inherits the uplifted rates from the parent invoice. The customer is refunded the amount they originally paid through the BNPL provider, not the pre-uplift rate. No manual adjustment is needed.

Audit fields on the invoice

💵 BNPL Settlement (Manual JE) Accounts

Used when Tabby/Tamara wires the net amount (gross − commission) to RMAX's bank account, typically 1–2 working days after the sale. Settlement is recorded as a plain Journal Entry (Bank Entry) with the three account heads pre-filled by the Load BNPL Settlement button.

Step-by-Step

  1. Open a new Journal Entry. Set Voucher Type = Bank Entry (or Journal Entry) and pick the Company.
  2. Click the Load BNPL Settlement button in the toolbar.
  3. In the dialog, pick the Mode of Payment (Tabby or Tamara). The dialog shows the resolved Clearing Account, Currency, and live GL Balance for context.
  4. Pick the Bank Account that received the provider's payout.
  5. Click Load Heads. The system clears any existing accounts table and inserts three rows: Dr Bank, Dr BNPL Fee Expense, Cr Clearing.
  6. Type the actual amounts from the Tabby / Tamara settlement statement: bank credit (net), fee (commission), clearing credit (gross = bank + fee). The Difference column should be zero.
  7. Optionally attach the provider's settlement statement PDF / CSV under Attachments.
  8. Click Save, review for any orange warnings, then Submit.
📊
Soft balance warning: if the credit to the BNPL Clearing account exceeds the live balance of that account, an orange warning appears on Save. This is informational only — submission is allowed. Verify against the provider statement before proceeding. Legitimate causes: refund carry-forward, statement timing differences, manual corrections.
Cancelling a Journal Entry reverses all three GL postings. There is no per-invoice "Settled" flag any more — reconciliation between BNPL invoices and settlement JEs happens via the BNPL Surcharge Collected report and standard Bank Reconciliation against the clearing account.

📊 BNPL Reports

BNPL Surcharge Collected

Lists submitted BNPL invoices with the SAR uplift attributed per Mode of Payment (Tabby / Tamara), based on the POS payment snapshot stamped at sale time. Use it to reconcile the surcharge collected against the provider's commission deductions on settlement statements — the two should match closely.

Filters: Company (required), From Date, To Date, Mode of Payment.

Columns: Sales Invoice, Posting Date, Customer, Mode of Payment, BNPL Uplift Attributed, Original Items Total.

🔒 No VAT Sale Sales Manager

The No VAT Sale doctype books a non-VAT-able cash sale. On submit it posts a Journal Entry (cash receipt) and a Stock Entry (Material Issue) against the company’s Naseef & Damage Written Off accounts.

🔒
Access restricted: Only System Manager and Sales Manager can read, write, submit, or cancel No VAT Sales. Branch User / Stock User / Sales User / Accounts Manager were removed from this doctype as of Apr 2026.

Approval Workflow

Every No VAT Sale moves through a four-state workflow before it submits:

StatusWho actsWhat happens
DraftCreatorFill items, pick branch + warehouse + approver, click Send for Approval.
Pending ApprovalNamed approverReceives a ToDo + email assignment. Clicks Approve & Submit (with optional remarks) or Reject (mandatory reason).
ApprovedSystemDoc is submitted. JE + SE post automatically.
RejectedCreatorDoc stays Draft with reason. Creator edits and resends.

Step-by-Step (Creator)

  1. Open No VAT Sale+ New.
  2. Pick Branch. The Warehouse dropdown is now restricted to that branch’s configured warehouses.
  3. Pick Warehouse, customer name, mode of payment.
  4. Add item rows. Selling rate auto-fetches from No VAT Price price list, valuation rate from the Bin.
  5. Pick Approved By. Required.
  6. Save the draft.
  7. Click Approval → Send for Approval. Status flips to Pending Approval and the approver gets notified.

Step-by-Step (Approver)

  1. Open the ToDo or click the email link.
  2. Review items, totals, and warehouse.
  3. Click Approval → Approve & Submit. Optional remarks dialog. Doc is submitted; JE + SE post.
  4. Or click Approval → Reject. Mandatory reason. Doc stays Draft, creator can edit + resend.
🔹
Sales Manager / System Manager can act as approver on any No VAT Sale even if they are not the named Approved By user.

👤 Create Customer (Quick)

Create a new customer directly from the Sales Invoice without leaving the form.

Step-by-Step

  1. On a new Sales Invoice, click the + Create New Customer button above the Customer field.
  2. Fill in the dialog:
    Customer Name (required)
    Mobile No (required)
    Email ID (optional)
    VAT Registration Number (optional, exactly 15 digits)
  3. If VAT number is entered, address fields become mandatory:
    • Address Line 1, Building Number, Area/District, City, Country, Postal Code
  4. Click Create Customer.
  5. The new customer is automatically set on the Sales Invoice.
Auto-created: The system creates both a Customer record and an Address record linked together. The address is set as both primary billing and shipping address.

📦 Warehouse Stock Panel

See real-time stock levels across all warehouses when selecting items on Sales Invoice.

How It Works

  1. On a Sales Invoice (or Quotation), click on an item row in the Items table.
  2. A stock panel appears below the items table showing stock quantities across all warehouses.
  3. The currently selected warehouse is highlighted.
  4. Click on any warehouse row to set that warehouse on the item.
  5. Click Show All to see all warehouses (default shows top 5).
📈
Stock Check: When you enter a quantity that exceeds available stock, the system will show a red alert and automatically reduce the quantity to the available stock.

🚚 Material Request (Transfer)

Request stock to be transferred from one warehouse to another. The default type is set to "Material Transfer".

Step-by-Step

  1. Open a New Material Request. The type defaults to Material Transfer.
  2. The Target Warehouse (Set Warehouse) auto-fills from your default warehouse permission.
  3. Select the Source Warehouse (Set From Warehouse). It is filtered to exclude the target warehouse.
  4. Add items and quantities. Schedule Date is auto-set to today.
  5. Save and Submit.

📦 Stock Transfer

Transfer stock between warehouses with an approval workflow. When approved, a Stock Entry is automatically created.

Step-by-Step (Requester)

  1. Open a New Stock Transfer.
  2. The Source Warehouse auto-fills from your default warehouse.
  3. Select the Target Warehouse (filtered to exclude source, ignores user permissions so you can send to any warehouse).
  4. Add items: select Item Code, set Quantity, and choose UOM (filtered to the item's available UOMs).
  5. Save to create as Draft.
  6. Click Submit to send for approval (state changes to Pending).

Step-by-Step (Approver)

  1. Open the Stock Transfer in Pending state.
  2. Review the items and quantities.
  3. Click Approve to approve — a Stock Entry (Material Transfer) is automatically created and submitted.
  4. Or click Reject to reject the transfer.
After approval, a link to the Stock Entry is shown in an alert. The Stock Entry name is also saved on the Stock Transfer record for reference.

📜 Purchase Receipt — Final GRN

Replace an existing Purchase Receipt with a corrected one. The original is automatically cancelled when the new one is submitted.

Step-by-Step

  1. Open the existing Purchase Receipt you want to replace.
  2. Click CreateFinal GRN.
  3. A new Purchase Receipt opens with all data copied:
    • Header fields (supplier, company, currency, etc.)
    • All item rows with quantities, rates, and warehouses
    • Tax rows
    • Posting time is set to 10 minutes before the original
  4. Make your corrections (edit quantities, items, etc.).
  5. Click Submit.
  6. The original Purchase Receipt is automatically cancelled (if submitted) or deleted (if draft).
Important: The auto-cancel only works when submitting from the same browser session where you clicked "Final GRN". If you navigate away and come back, you'll need to cancel the original manually.

📋 Bulk Purchase Invoice

Create a single Purchase Invoice from multiple Purchase Receipts at once.

Step-by-Step

  1. Go to the Purchase Receipt list.
  2. Select multiple Purchase Receipts using the checkboxes (they must be from the same supplier).
  3. Click ActionsCreate Single Purchase Invoice.
  4. Confirm the action. A single Purchase Invoice is created with all items from all selected receipts.
  5. A success message with a link to the new Purchase Invoice appears.

📄 Quotation to Sales Invoice

Convert a submitted Quotation directly into a Sales Invoice.

Step-by-Step

  1. Open a submitted Quotation.
  2. Click CreateSales Invoice.
  3. A new Sales Invoice opens with all items and details mapped from the Quotation.
  4. Review, adjust if needed, and Save/Submit.

🖶 Landed Cost Voucher — CBM Distribution

Distribute landed costs across items proportionally based on their CBM (Cubic Meters) values, instead of the standard Qty or Amount methods.

Step-by-Step

  1. Open a Landed Cost Voucher and add the Purchase Receipt(s).
  2. Set Distribute Charges Based On to Distribute Manually.
  3. Check the Distribute by CBM checkbox (appears after selecting Distribute Manually).
  4. In the items table, enter the CBM value for each item.
  5. Add the tax/charge row in the Taxes and Charges table.
  6. The Applicable Charges column auto-fills proportionally based on each item's CBM ratio.
  7. Save and Submit.
📊
Calculation: Each item's applicable charge = (Item CBM / Total CBM) × Total Charges. Any rounding difference is adjusted on the last item automatically.
🚀
Multiple tax rows supported with CBM: When Distribute by CBM is checked you can add any number of tax/charge rows (e.g. Freight + Duty + Doc Charges). Each row is split across items by the CBM ratio and each expense account receives its own GL entry.

📋 LCV Charge Template & Purchase Receipt Checklist

Load the standard import charges (Freight, Duty, Port, Mawani, etc.) onto a Purchase Receipt with one click, and track whether every charge has been booked via a Landed Cost Voucher.

Pick a template on the PR

  1. Open any Purchase Receipt and expand the LCV Charges Checklist section.
  2. Select LCV Charge Template — the default Standard Import KSA ships with 11 charges.
  3. Save the PR. The checklist populates with each charge + the correct expense account for the PR's company.
  4. A coloured indicator appears at the top of the form showing the status:
    • Not Started — no charge booked yet (grey).
    • Pending — mandatory charges missing (red).
    • Partial — mandatory charges done, optional still pending (orange).
    • Complete — every charge booked (green).

Create the LCV from the template

  1. From the PR, click LCV Checklist → Create LCV from Template.
  2. A Draft Landed Cost Voucher opens pre-populated with only the pending charges (already-booked rows are skipped).
  3. If every pending charge is CBM-based, the LCV is pre-set to Distribute Manually + Distribute by CBM.
  4. Enter the actual shipment amounts for each row and submit.
  5. Back on the PR, the indicator flips to Complete and each checklist row shows the LCV reference + amount.
📈
Reuse after submission: The template and status fields can be changed even after the PR is submitted, so you can book charges over several days as bills arrive.
Currency: The Freight Sea/Air account ships in USD. When the LCV is created, the Freight row shows an Exchange Rate field; enter the rate so the SAR base amount posts correctly.

Standard Import KSA — shipped charges

Every Company gets these 11 expense accounts (under Indirect Expenses → Landed Cost Charges) and one template row each:

#ChargeCurrencyDistribute By
1Freight Sea/AirUSDCBM
2DutySARValue
3DO ChargesSARCBM
4Port ChargesSARCBM
5MawaniSARCBM
6Fasah Appointment FeesSARCBM
7Custom Clearance ChargesSARCBM
8Transportation to WarehouseSARCBM
9Doc ChargesSARCBM
10Local Unloading ExpenseSARCBM
11Overtime (Kafeel)SARCBM

Duplicate the template if you need a variant (e.g. Air-only, road freight) — copy Standard Import KSA, rename, tweak rows. The app will never overwrite a template that isn't the shipped default.

Example — one shipment, three items

ItemCBMShare of 100
LED Panel550%
Spot Light330%
UFO 300W220%

If Freight = 2,500 SAR (after exchange), Port = 800 SAR, Mawani = 300 SAR (total 3,600 SAR), the app distributes each charge by the CBM share:

  • LED Panel absorbs 1,800 SAR (50%)
  • Spot Light absorbs 1,080 SAR (30%)
  • UFO 300W absorbs 720 SAR (20%)

Stock valuations (Stock In Hand Dr) increase by those amounts; each expense account (Freight, Port, Mawani) is credited with its own GL entry.

✅ VAT & Contact Validation

The system enforces Saudi VAT and phone number rules automatically.

VAT Rules (Customer)

RuleDetail
Digits onlyOnly numeric characters allowed in VAT field
Exactly 15 digitsVAT must be exactly 15 digits to save
UniqueNo two customers (except "Branch" type) can have the same VAT number

Phone Number Rules

RuleDetail
Minimum 10 digitsBoth mobile and phone numbers must have at least 10 digits
Validated onCustomer form (primary contact) and Contact form (all phone numbers)

Customer Type

Customer Type options include: Company, Individual, Partnership, Branch.

🔒
The "Branch" option is only visible to users with System Manager or Auditor roles. Regular users will not see it.

Duplicate VAT — Sales Manager Override

Genuine cases exist where two Customer records share one VAT number (e.g. subsidiaries under one parent legal entity, the same VAT-registered chain invoicing under two storefronts). Without an override the system rejects the second Customer with:

VAT Registration Number already used by Customer: CUST-0042. A Sales Manager can tick 'Allow Duplicate VAT' to override.

Roles allowed to unlock: Sales Manager, Sales Master Manager, System Manager.

  1. Open the new Customer (the one you are trying to save).
  2. Tick Allow Duplicate VAT (Manager Override).
  3. Fill Duplicate VAT Reason — mandatory. Example: "Branch of CUST-0042, same legal entity, separate storefront".
  4. Save. The duplicate check is skipped for this Customer only.
Branch User / Sales User / Stock User do not see the override fields. Asking them to "just override" will not work — escalate to a Sales Manager or System Manager.

💰 Sales Invoice Defaults & Update Stock Lock

Every new Sales Invoice already ships ready for over-the-counter sales — operators only pick customer + items.

What is pre-filled on a new SI

FieldDefault
Update StockTicked on
Source WarehouseUser's default warehouse (from their branch configuration)

Branch User lock

For plain Branch Users the Update Stock field is read-only. This prevents accidental credits that skip stock movement. A description appears under the field: “Locked for Branch Users. Contact a Sales Manager to change.”

Users who hold any of these additional roles can still untick the field when needed: Sales Manager, Sales Master Manager, Stock Manager, System Manager, Administrator.

Why this matters

When Update Stock is ticked, submitting the Sales Invoice reduces stock in the selected warehouse immediately — no separate Delivery Note needed. Useful for counter / cash sales.

When it is unticked, stock stays put and you are expected to create a Delivery Note to dispatch goods later (credit-sale flow).

📈
Default behaviour: ticked on — stock moves on submission. Branch User cannot change this. If a specific deal needs credit sale with a Delivery Note, a Sales Manager must create / edit the invoice.

💼 HR Defaults (Sponsorship / Non-Sponsorship) Admin

The app ships a first-run HR seed so the client can start running payroll immediately on new companies.

What gets created

  • Employee Grades: Sponsorship, Non-Sponsorship.
  • Salary Components: Basic, Housing Allowance, Transportation Allowance, Food Allowance, Other Allowance, GOSI Employee.
  • Salary Structure: RMAX Sponsorship KSA - <CompanyAbbr> per Company, in Draft. Split shown below.
  • No default structure for Non-Sponsorship — attach a custom one per employee if needed.

Sponsorship structure — sample payout on base 10,000 SAR

ComponentFormulaAmount (SAR)
Basicbase * 0.606,000
Housing Allowancebase * 0.252,500
Transportation Allowancebase * 0.101,000
Food Allowancebase * 0.05500
Gross10,000

Enter a monthly base figure on the Salary Structure Assignment for an employee and the components compute automatically. GOSI Employee (deduction) is not pre-filled — add it per employee nationality (Saudi / GCC / expat rules differ).

What to do on day one

  1. Open HR → Employee Grade — confirm Sponsorship / Non-Sponsorship exist.
  2. Open Salary Structure → RMAX Sponsorship KSA - <CompanyAbbr> (Draft). Review earnings, add deductions if required (GOSI, advances, etc.), then Submit.
  3. For each expat employee, create a Salary Structure Assignment picking the shipped structure + entering their monthly base.
  4. Non-Sponsorship employees: create a matching Salary Structure (free form) and assign it separately.
🔒
Safe for manual edits: Once a grade / component / structure exists, the app never touches it again. Change amounts, add components, submit the structure — nothing is overwritten on the next bench migrate.
🛡
Reset (destructive): An admin can re-seed by running bench --site <site> execute rmax_custom.hr_defaults.reset_sponsorship_salary_structures --kwargs '{"force": 1}'. The helper refuses to delete submitted structures or those already assigned to an employee.

🔄 Inter-Company Delivery Notes → Consolidated Sales Invoice Accounts

RMAX trades stock between sister Companies (e.g. Head Office ↔ Clearlight Munavar). Goods move via Delivery Notes first, then accounts batches multiple DNs into a single Sales Invoice on the selling side. The system auto-creates the matching Purchase Invoice on the buying side.

🔹
Difference from Inter-Branch R/P: This is cross-Company (different legal entities, separate books). Inter-Branch R/P is intra-Company (one company, multiple branches). Don’t confuse the two.

Setup (one-time)

  1. Open Inter Company Branch master → create one row per (Selling Company, Buying Company) pair. Set the cost center + warehouse the buying side will use when its PI is auto-created.
  2. Ensure the Inter Company Price price list exists (auto-created by after_migrate) and item rates are loaded for inter-company prices.
  3. Mark the customer record as Internal Customer + set Represents Company on the customer for the buying side.

Daily Flow

  1. Branch creates Delivery Note(s) as usual. For inter-company DNs, tick Is Inter-Company and set Inter-Company Branch on the DN. Submit (stock leaves the selling company’s warehouse).
  2. Accounts opens the Delivery Note list. Select 2+ submitted inter-company DNs that share the same Customer, Represents Company, Currency, and Inter-Company Branch.
  3. Click the list action Create Inter-Company SI.
  4. System builds one Draft Sales Invoice on the selling side:
    • Taxes inherited from the first selected DN.
    • Cost Center + Source Warehouse pulled from Inter Company Branch for the selling Company.
    • Update Stock = OFF (DNs already moved stock; this SI is purely the inter-company invoice).
    • Each row mirrors the underlying DN row at the inter-company Price List rate.
  5. Each source DN gets stamped: Inter-Company SI = the new SI, Inter-Company Status = Consolidated. They are now locked from being batched again.
  6. Review the Draft SI → Submit. On submit:
    • System auto-creates the matching Purchase Invoice on the buying Company.
    • Buying side’s PI uses the cost center + warehouse from Inter Company Branch.
    • If the SI is a return, the system links to the original PI and sets Is Return on the PI side too.
Cancel symmetry: Cancelling the consolidated SI clears the Inter-Company SI stamp on every source DN and resets Inter-Company Status to Not Consolidated. Those DNs become eligible for a fresh batch. The auto-created PI must be cancelled separately by accounts on the buying side.

Validation Rules

  • Selected DNs must be submitted (docstatus=1).
  • Each must have Is Inter-Company ticked.
  • All must share the same Customer, Represents Company, Currency, and Inter-Company Branch.
  • None already linked to a non-cancelled SI via Inter-Company SI.
  • Inter Company Branch master must have a row for the selling Company with valid Cost Center + Warehouse for the buying side.

Any violation aborts the batch with a clear message.

⚠ Damage Workflow Branch + Damage User

Three-step process to move damaged stock out of branch warehouses, inspect, and write off the loss to a dedicated GL account. Tracks supplier code + photo evidence per item for audit.

Roles & DocTypes

DocTypeNamingCreated byAction
Damage SlipDS-#####Branch UserRecords damaged items at the branch warehouse.
Damage TransferDT-#####Damage UserPulls in pending slips, inspects, posts Stock Entry to Damage warehouse.
Stock Entry (Material Issue)auto-createdSystem / AdminWrite-off to Damage Loss Account.

Step 1 — Branch User: Damage Slip

  1. Open Damage Slip → New.
  2. Pick Branch Warehouse (the branch warehouse where the damaged stock currently sits).
  3. Pick Damage Warehouse (filtered to Damage Jeddah - CNC or Damage Riyadh - CNC).
  4. Add item rows (item code + qty).
  5. Save + Submit. Status flips to Pending.

Step 2 — Damage User: Damage Transfer (inspection)

  1. Open Damage Transfer → New.
  2. Pick the Damage Warehouse.
  3. Click Get Pending Slips — pulls in submitted Damage Slips for that warehouse.
  4. For each item row: attach at least one image (proof of damage) and pick the Supplier Code (Clear Desk / Clear Light / RMAX / Clear Desk USD).
  5. Save + Submit. The system auto-creates a Stock Entry (Material Transfer) moving items from the branch warehouse → Damage warehouse.
  6. Linked Damage Slips switch from Pending to Transferred.
Photo + Supplier Code mandatory: Submission is rejected if any item row is missing either. Inspection is the audit trail — do not skip.

Step 3 — Admin: Write Off

  1. Open the submitted Damage Transfer.
  2. Click Write Off (top-right action button).
  3. System creates a Stock Entry (Material Issue): Dr Damage / Loss Account, Cr Stock In Hand. Quantity leaves the Damage warehouse permanently.
  4. Damage Transfer status → Written Off.
🔹
Configuration check: Each Company must have Damage Loss Account set on its Company record before Write Off works. If unset, the Write Off button throws an error.

Selling Damaged Stock as Scrap

If damaged items still have salvage value (e.g. metal scrap, returnable shells), see the Scrap Sale section. Sell from the Damage warehouse (where items sit after Damage Transfer) instead of writing them off — preserves the recoverable value as scrap revenue.

📱 Damage PWA — Mobile App Damage User

The Damage PWA lets Damage Users capture inspection photos and submit Damage Transfers from a mobile device without opening the full ERP desk.

Install on Android (APK)

  1. Download the latest APK from GitHub Releases → v1.0.19.
  2. On the device: Settings → Security → Install from unknown sources (enable).
  3. Open the downloaded rmax-wh-v1.0.19.apk and tap Install.
  4. App name: RMAX WH. Points to rmaxerp.enfonoerp.com.

Each update is a new APK download. Check Releases page for the latest version.

Install on iOS / Any Browser (PWA)

  1. Open Safari on the iPhone (must be Safari for Add to Home Screen).
  2. Go to: https://rmaxerp.enfonoerp.com/damage-pwa
  3. Tap the Share icon → Add to Home Screen.
  4. The PWA appears as an app icon on the home screen.

Login

  • Use your ERP username and PIN (set under your User profile in the ERP).
  • The app works online only — an active internet connection is required.
🔗
APK version history: All releases at github.com/EnfonoTech/damage-pwa/releases. Current production release: v1.0.19 (2026-05-10).

📊 Inter-Branch Receivables & Payables Accounts

When a single Company runs multiple branches (Head Office, Riyadh, Jeddah, Snowlite, Malaz, Bahra…), every cross-branch transaction must show in each branch’s own books even though the consolidated company books stay balanced. This module captures those obligations automatically — you record the actual business event, the system adds the matching Due-from / Due-to legs in the background.

🔒
Activation gate: The auto-injector is disabled until Inter-Branch Cut-Over Date is set on the Company. Until then, every Journal Entry posts unchanged. Set this only when the inter-branch account heads are reviewed and ready.

One-Time Setup

  1. Foundation accounts (Inter-Branch Receivable — Asset, group, under Current Assets — and Inter-Branch Payable — Liability, group, under Current Liabilities) are created automatically per root Company by after_migrate.
  2. Per-counterparty leaf accounts (Due from <Branch>, Due to <Branch>) are created lazily the first time they’re needed, plus immediately when a new Branch is added.
  3. Open Company → pick the Company → set Inter-Branch Cut-Over Date. Auto-injector is now ON for entries on/after this date.
  4. For multi-branch (3+) Journal Entries, set Inter-Branch Bridge Branch on the Company. Every other branch in the JE pairs against the bridge.

Scenario A — HO pays rent for Branch Riyadh

  1. Open Accounting → Journal Entry → New.
  2. Pick Posting Date and Company.
  3. Add line 1: Rent Expense, Debit 1,000, Branch = Riyadh.
  4. Add line 2: HO Bank Account, Credit 1,000, Branch = HO.
  5. Save.

The system auto-adds:

AccountBranchDrCr
Rent ExpenseRiyadh1000
HO Bank AccountHO1000
Due to HORiyadh1000
Due from RiyadhHO1000

Submit the JE. Each branch’s books balance independently; consolidated still nets to zero.

Scenario B — Cash transfer HO → Branch

Same flow as A: enter Riyadh Bank Dr 5000, HO Bank Cr 5000. System adds the Due-from/Due-to legs.

Scenario C — Stock transfer between branches (automatic)

Two routes, identical accounting:

  • Stock Transfer workflow (preferred for branch users): Material Request → Stock Transfer → Approval. On approval, the system creates the Stock Entry as usual. If source warehouse and target warehouse belong to different branches, a companion Journal Entry records the inter-branch obligation at valuation cost. Source DocType = Stock Transfer.
  • Direct Stock Entry (Material Transfer): Stock Manager opens Stock Entry directly. On submit the system resolves each warehouse → Branch via Branch Configuration. Same-branch warehouse pair (e.g. WH-HO-1 ↔ WH-HO-2 both under HO) — no companion JE. Cross-branch — system re-tags the SE’s own GL legs by branch and creates the companion JE. Source DocType = Stock Entry.

Cancelling the source document (Stock Transfer or Stock Entry) auto-cancels the companion JE.

🔎
Auto-inserted line markers: every auto-generated JE Account row carries Auto-Inserted (Inter-Branch) ticked, Source DocType = the originating doctype, Source Document = the originating doc name. Read-only; for traceability and audit.

Multi-Branch (3+) JEs — Bridge Mode

If a single JE involves 3 or more branches, the system needs an explicit pivot to know how to pair Due-from/Due-to legs. Set Inter-Branch Bridge Branch on the Company (typically HO) and ensure the bridge is one of the branches in the JE. Every non-bridge branch is then paired against the bridge.

Without a bridge configured, the system rejects 3+ branch JEs with a clear error so you don’t accidentally post unbalanced per-branch books.

Reconciliation Report

Open Reports → Inter-Branch Reconciliation. Matrix view: rows = from branch, columns = to branch, cells = net Due balance for the period. Health check: every (A→B + B→A) pair must sum to zero — non-zero rows flag missing reversals or unmatched legs.

Roles: Accounts Manager, Accounts User, Auditor, System Manager.

🔹
Out of scope (Phase 1): Settlement / clearing; salary, expense claim, vendor-on-behalf flows; branch-wise TB / P&L / BS reports beyond reconciliation; HO overhead allocation; historical restate. Coming in later phases.

⌨ Enter Key Navigation

The Enter key works as a navigation key across all forms and doctypes in the system.

How It Works

ContextEnter Key Behavior
Main form fieldMove to the next visible, editable field
Child table (grid) — not last columnMove to next column in the same row
Child table — last columnMove to first column of next row
Child table — last column, last rowAdd a new row and move to it
TextareaNormal newline (not intercepted)
Ctrl/Cmd + EnterNormal submit shortcut (not intercepted)
Button / DialogNormal click (not intercepted)
This works on all doctypes — Sales Invoice, Purchase Receipt, Quotation, Material Request, Stock Transfer, and any other form.

🔍 Branch-wise List Filtering

Branch Users automatically see only documents related to their branch. No manual filtering needed.

Filtered DocTypes

DocTypeFilter Logic
Sales InvoiceItems warehouse matches branch warehouse, OR user is the creator
Purchase InvoiceItems warehouse matches branch warehouse, OR user is the creator
Delivery NoteItems warehouse matches branch warehouse, OR user is the creator
Purchase ReceiptItems warehouse matches branch warehouse, OR user is the creator
Payment EntryUser is the creator
QuotationUser is the creator
🔒
Admin Access: System Manager, Stock Manager, and Administrator bypass all filters and see all documents.

Sales Invoice List — Extra Filters

The Sales Invoice list view includes additional standard filters in the header:

FilterDescription
Grand TotalFilter invoices by total amount (with tax)
Total QtyFilter by total quantity of items
Contact MobileSearch by customer mobile number

🛠 Architecture Overview Developer

Technical architecture of the RMAX Custom app for ERPNext v15.

App Structure

rmax_custom/
├── hooks.py                          # App config: JS includes, doc_events, fixtures, permissions
├── setup.py                          # after_migrate: Branch User role + DocType permissions
├── inter_company.py                  # Auto PI creation on SI submit
├── landed_cost.py                    # LCV distribution helper
├── branch_defaults.py                # Cost center override for branch users
├── branch_filters.py                 # permission_query_conditions for list filtering
├── overrides/
│   └── landed_cost_voucher.py        # LCV class override (CBM distribution)
├── api/
│   ├── customer.py                   # Quick customer creation + VAT validation
│   ├── sales_invoice_payment.py      # POS payment popup backend
│   ├── warehouse_stock.py            # Stock balance across warehouses
│   ├── material_request.py           # MR creation API
│   ├── purchase_invoice.py           # Bulk PI from multiple PRs
│   └── item.py                       # Party Specific Item creation
├── public/js/
│   ├── enter_navigation_global.js    # Global Enter key → Tab behavior
│   ├── warehouse_stock_popup.js      # Stock panel below items grid
│   ├── sales_invoice_pos_total_popup.js  # POS payment dialog
│   ├── sales_invoice_popup.js        # SI form enhancements
│   ├── create_customer.js            # Quick customer dialog on SI
│   ├── create_multiple_supplier.js   # Bulk supplier linking on Item
│   ├── materiel_request.js           # MR warehouse defaults
│   ├── vat_validation.js             # Customer VAT + phone rules
│   ├── contact_validation.js         # Contact phone validation
│   ├── item_branch_user.js           # Hide cost fields for Branch User
│   ├── purchase receipt.js           # Final GRN flow
│   ├── purchase_receipt_list.js      # Bulk PI from list
│   ├── material_request_list.js      # Urgent indicator
│   ├── sales_invoice_list.js         # Extra list filters
│   └── landed_cost_voucher.js        # CBM distribution client
├── rmax_custom/
│   ├── doctype/
│   │   ├── branch_configuration/     # Branch → Company/WH/CC/Users mapping
│   │   ├── inter_company_branch/     # Inter-company CC/WH config
│   │   ├── stock_transfer/           # Custom transfer with approval workflow
│   │   └── (child tables)            # Config User/WH/CC, ST Item, IC Branch CC
│   └── custom_scripts/quotation/     # Create SI from Quotation button
└── fixtures/
    ├── workflow.json                  # Stock Transfer workflow (4 states)
    ├── role.json                      # Branch User role definition
    ├── custom_field.json              # Custom fields (payment mode, CBM, VAT, urgent)
    └── property_setter.json           # Field tweaks (MR defaults, SI filters, customer type)

Data Flow Diagrams

Sales Invoice (Cash) Flow

User creates SI (Cash mode)
  → Items added (stock check + warehouse popup)
  → Click Submit → Payment Popup appears
  → Allocate amounts across payment modes
  → "Save & Submit"
      → before_validate: cost center override (branch_defaults.py)
      → SI saved + submitted
      → Payment Entries auto-created (one per mode)
      → on_submit: if inter-company → auto-create draft PI (inter_company.py)

Branch Configuration Flow

Admin saves Branch Configuration
  → validate(): check WH/CC belong to Company
  → before_save(): delete permissions for removed users
  → on_update() → create_permissions():
      → User Permission: Company (is_default=1)
      → User Permission: Branch
      → User Permission: Warehouse (first=default)
      → User Permission: Cost Center (first=default)
      → User Permission: Company default CC (is_default=0, access only)
      → Assign "Branch User" role to user

Stock Transfer Approval Flow

User creates Stock Transfer
  → Draft → Send For Approval → Pending
  → Target branch user opens it
  → validate(): check user in target branch's Branch Configuration
  → Approve → on_submit() → create Stock Entry (Material Transfer)
  → OR Reject → stays as draft

🔗 Hooks & Events Developer

All Frappe hooks configured by the app.

app_include_js (loaded on every desk page)

FilePurpose
enter_navigation_global.jsGlobal Enter key → Tab navigation
warehouse_stock_popup.jsStock display panel below items grid
sales_invoice_pos_total_popup.jsPOS payment popup for Cash mode
sales_invoice_popup.jsSI: New Invoice button, update_stock, stock check
create_customer.jsQuick customer creation from SI
create_multiple_supplier.jsBulk supplier linking on Item
materiel_request.jsMR: default warehouse, query filters
vat_validation.jsCustomer VAT + phone validation
contact_validation.jsContact phone validation
item_branch_user.jsHide cost fields for Branch User

doc_events (server-side hooks)

DocTypeEventHandlerLogic
Sales Invoicebefore_validatebranch_defaults.override_cost_center_from_branchReplace inaccessible cost centers with user's default
Sales Invoiceon_submitinter_company.sales_invoice_on_submitAuto-create draft Purchase Invoice for internal customers
Purchase Invoicebefore_validatebranch_defaults.override_cost_center_from_branchSame cost center override
Payment Entrybefore_validatebranch_defaults.override_cost_center_from_branchSame cost center override
Delivery Notebefore_validatebranch_defaults.override_cost_center_from_branchSame cost center override
Purchase Receiptbefore_validatebranch_defaults.override_cost_center_from_branchSame cost center override

permission_query_conditions (list filtering)

DocTypeHandlerLogic
Sales Invoicebranch_filters.si_permission_queryFilter by branch WH (items/set_warehouse) OR owner
Purchase Invoicebranch_filters.pi_permission_querySame warehouse-based filter
Delivery Notebranch_filters.dn_permission_querySame warehouse-based filter
Purchase Receiptbranch_filters.pr_permission_querySame warehouse-based filter
Payment Entrybranch_filters.pe_permission_queryOwner-only filter
Quotationbranch_filters.quotation_permission_queryOwner-only filter

override_doctype_class

DocTypeOverride ClassPurpose
Landed Cost Voucherrmax_custom.overrides.landed_cost_voucher.LandedCostVoucherCBM-based charge distribution

after_migrate

HandlerPurpose
rmax_custom.setup.after_migrateCreate/update Custom DocPerm records for Branch User role (14 DocTypes)

🔌 API Reference Developer

All whitelisted API endpoints provided by the app.

Customer APIs

EndpointParametersReturns
rmax_custom.api.customer.create_customer_with_addresscustomer_name, mobile_no, email_id, address fields, VAT, country{customer, address, message}
rmax_custom.api.customer.validate_vat_customervat, customer_type, nameThrows if duplicate
rmax_custom.api.customer.validate_phone_numbersmobile_no, phone_noThrows if <10 digits

Payment APIs

EndpointParametersReturns
rmax_custom.api.sales_invoice_payment.get_payment_modes_with_accountcompany, mode_list (optional)List of valid mode names
rmax_custom.api.sales_invoice_payment.create_pos_payments_for_invoicesales_invoice, payments (JSON array)List of created PE names

Stock & Item APIs

EndpointParametersReturns
rmax_custom.api.warehouse_stock.get_item_warehouse_stockitem_code, company, limit, target_warehouseList of {warehouse, stock_qty}
rmax_custom.api.material_request.create_material_requestitem_code, from/to warehouse, qty, schedule_date, companyMR name
rmax_custom.api.purchase_invoice.create_single_purchase_invoicereceipt_names (JSON array)PI name
rmax_custom.api.item.create_party_specific_itemsitem, suppliers (JSON array)"Done"
rmax_custom.rmax_custom.doctype.stock_transfer.stock_transfer.get_item_uom_conversionitem_code, uomConversion factor (float)

🔒 Permission System Developer

How the multi-layer permission system works together.

Layer 1: User Permissions (from Branch Configuration)

When a Branch Configuration is saved, these User Permissions are created per user:

AllowValueis_defaultPurpose
CompanyBranch company1Default company for new docs
BranchBranch name0Branch access
WarehouseFirst branch WH1Default warehouse
WarehouseOther branch WHs0Access only
Cost CenterFirst branch CC1Default cost center
Cost CenterOther branch CCs0Access only
Cost CenterCompany default CC0Access for tax templates (e.g. Main - CNC)

Layer 2: DocType Permissions (from setup.py after_migrate)

Custom DocPerm records control which DocTypes the Branch User can read/write/create/submit. These are created programmatically on every bench migrate.

Layer 3: permission_query_conditions (from branch_filters.py)

SQL WHERE clauses injected into list queries to filter documents by branch warehouses. Only affects Branch User role; Admin/Stock Manager bypass.

Logic for SI/PI/DN/PR:

WHERE (
  set_warehouse IN (user's branch warehouses)
  OR name IN (SELECT parent FROM items WHERE warehouse IN (user's WHs))
  OR owner = current_user
)

Layer 4: Cost Center Override (from branch_defaults.py)

Server-side before_validate hook that replaces cost centers the user doesn't have access to. Catches cases where a tax template or item default injects a cost center the branch user can't access.

Layer 5: Client-side Restrictions (from item_branch_user.js)

Hides cost/valuation fields on Item form for Branch Users. Checks frappe.user_roles and only applies if user has Branch User but NOT Stock Manager or System Manager.

📁 Fixtures & Custom Fields Developer

All fixtures exported by the app.

Custom Fields

NameTypePurpose
Sales Invoice → custom_payment_modeSelect (Cash/Credit)Drives POS popup vs Credit behavior
Sales Invoice → custom_inter_company_branchLinkSelects branch for inter-company PI
Quotation → custom_payment_modeSelectPayment mode on quotation
Customer → custom_vat_registration_numberData15-digit Saudi VAT number
LCV → custom_distribute_by_cbmCheckEnable CBM distribution
Landed Cost Item → custom_cbmFloatCBM value per item
Material Request → custom_is_urgentCheckUrgent priority flag

Workflows

WorkflowDocTypeStates
Stock Transfer WorkflowStock TransferDraft → Pending → Approved (docstatus=1) / Rejected

Key: Approval is branch-based — only users from the target warehouse's Branch Configuration can approve/reject. Validated in stock_transfer.py → validate().

Dependencies

AppUsage
ERPNext v15Core ERP (SI, PI, PE, Stock Entry, etc.)
sf_tradingapply_patch/restore_patch for inter-company PI creation (bypasses session defaults)

♻ Scrap Sale Workflow

Use this workflow to sell damaged, written-off, or end-of-life items as scrap to an external buyer. This keeps inventory accurate and records the scrap revenue separately.

1
Confirm the items are written off

Before a scrap sale, the items must already be written off (removed from active stock) via a Damage Transfer & write-off Stock Entry, or a direct Stock Entry (Material Issue). Verify in Stock Balance that the qty is zero in the source warehouse.

2
Create a Sales Invoice for the scrap buyer

Go to Accounting → Sales Invoice → New. Select or create the scrap buyer as Customer. Add the scrap items with the agreed scrap selling price. Set warehouse to the Damage warehouse (e.g. Damage Jeddah - CNC) where the items currently sit.

3
Set Update Stock = Yes

In the Sales Invoice, enable Update Stock so that submitting the invoice automatically reduces the stock from the damage warehouse. This creates a Stock Ledger Entry deducting the scrap qty.

4
Submit the Sales Invoice

On submit, the system:
• Debits Accounts Receivable (or Cash)
• Credits Sales / Scrap Revenue account
• Reduces stock qty from the damage warehouse

5
Record Payment

Create a Payment Entry against the Sales Invoice to mark it as paid. Link it to the scrap buyer and the correct bank/cash account.

Note: Scrap items sold this way must not be in regular branch warehouses. They should have been transferred to the Damage warehouse first via the Damage Transfer workflow before the sale is recorded.

📋 Purchase Invoice — Non-Stock Items Important

When purchasing items that have already been written off (e.g. returned damaged goods, scrap repurchase, or cost adjustments), create the Purchase Invoice as a non-stock item transaction. This prevents double-counting in inventory.

Key Rule: These items are already written off from stock — they must NOT update inventory again. Always set Update Stock = No (or use a non-stock item) on these Purchase Invoices.
1
Create a New Purchase Invoice

Go to Buying → Purchase Invoice → New. Select the supplier. Do NOT tick "Update Stock" — leave it unchecked.

2
Add Items as Non-Stock or Expense

Add the written-off items to the invoice. Since these are already written off, set the Expense Account on each item line to an appropriate expense or loss account (e.g. Cost of Goods Sold or Write-Off Expense). Do not set a warehouse.

3
Set Cost Center

The system will automatically set the Cost Center from the branch configuration. Verify it reflects the correct branch before submitting.

4
Submit

Submit the Purchase Invoice. This records the liability to the supplier without touching stock. The accounting entry will be:
• Debit: Expense / Loss account
• Credit: Accounts Payable (Supplier)

5
Pay the Supplier

Create a Payment Entry against this Purchase Invoice to record the payment to the supplier.

ScenarioUpdate StockExpense Account
Regular purchase (new stock)YesStock / COGS
Written-off / scrap repurchaseNoWrite-Off or COGS
Service / freight / expenseNoRelevant expense account

RMAX Custom App — User & Developer Guide — Built by Enfono