Company Context
PROTOTYPE MODE
Logged in as
Deliverable #3

Architecture Explanation

Written explanation of the architecture, data flow, and design decisions for both validation tasks.

System Architecture Overview

┌─────────────────────────────────────────────────────────────────┐
│                    EXTERNAL PLANNING INTERFACE                   │
│                    (React + TypeScript SPA)                       │
│                                                                   │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────────┐   │
│  │  Allocation   │  │  Migration   │  │   Reconciliation     │   │
│  │  Interface    │  │  Dashboard   │  │   Reports            │   │
│  │  (Drag&Drop)  │  │  (Status)    │  │   (Matrix)           │   │
│  └──────┬───────┘  └──────┬───────┘  └──────────┬───────────┘   │
│         │                  │                      │               │
│  ┌──────┴──────────────────┴──────────────────────┴───────────┐  │
│  │              Company Context Provider                       │  │
│  │         (Permission Segregation Layer)                      │  │
│  └──────────────────────┬─────────────────────────────────────┘  │
└─────────────────────────┼───────────────────────────────────────┘
                          │
                    HTTP/REST API
                    (Token Auth)
                          │
┌─────────────────────────┼───────────────────────────────────────┐
│                  FRAPPE/ERPNEXT SERVER                            │
│                                                                   │
│  ┌──────────────────────┴─────────────────────────────────────┐  │
│  │              Whitelisted API Methods                        │  │
│  │         (@frappe.whitelist() decorated)                     │  │
│  └──────────────────────┬─────────────────────────────────────┘  │
│                          │                                        │
│  ┌──────────────────────┴─────────────────────────────────────┐  │
│  │              Server-Side Validation Layer                   │  │
│  │  • Permission checks (has_permission)                      │  │
│  │  • Duplicate detection (frappe.db.exists)                  │  │
│  │  • Capacity validation (aggregate queries)                 │  │
│  │  • Company segregation (user_permissions)                  │  │
│  └──────────────────────┬─────────────────────────────────────┘  │
│                          │                                        │
│  ┌──────────────────────┴─────────────────────────────────────┐  │
│  │              Frappe ORM Layer                               │  │
│  │  • frappe.get_doc() / frappe.new_doc()                     │  │
│  │  • doc.insert() / doc.save() / doc.submit()                │  │
│  │  • No direct SQL writes                                    │  │
│  └──────────────────────┬─────────────────────────────────────┘  │
│                          │                                        │
│  ┌──────────────────────┴─────────────────────────────────────┐  │
│  │              MariaDB Database                               │  │
│  │  • tabLine Allocation                                      │  │
│  │  • tabProduction Line                                      │  │
│  │  • tabSales Order (core)                                   │  │
│  └────────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────────┘

Custom Frappe App Structure

shangu_erp/
├── shangu_erp/
│   ├── __init__.py
│   ├── hooks.py
│   ├── api.py                    # Whitelisted methods
│   ├── shangu_manufacturing/
│   │   ├── doctype/
│   │   │   ├── production_line/
│   │   │   │   ├── production_line.json
│   │   │   │   └── production_line.py
│   │   │   └── line_allocation/
│   │   │       ├── line_allocation.json
│   │   │       └── line_allocation.py  # Validation
│   │   └── __init__.py
│   └── public/
│       └── js/
│           └── allocation_board.js
├── setup.py
└── requirements.txt

Line Allocation DocType Definition

{
  "doctype": "DocType",
  "name": "Line Allocation",
  "module": "Shangu Manufacturing",
  "is_submittable": 1,
  "fields": [
    {
      "fieldname": "sales_order",
      "fieldtype": "Link",
      "options": "Sales Order",
      "reqd": 1,
      "unique": 1
    },
    {
      "fieldname": "production_line",
      "fieldtype": "Link",
      "options": "Production Line",
      "reqd": 1
    },
    {
      "fieldname": "allocated_quantity",
      "fieldtype": "Int",
      "reqd": 1
    },
    {
      "fieldname": "company",
      "fieldtype": "Link",
      "options": "Company",
      "reqd": 1
    }
  ],
  "permissions": [
    {
      "role": "Manufacturing Manager",
      "read": 1, "write": 1,
      "create": 1, "delete": 1,
      "match": "company"
    }
  ]
}

Allocation Data Flow

1
User Initiates Drag

User drags a Sales Order card from the unallocated panel. The frontend captures the Sales Order ID.

HTML5 Drag and Drop API, React state management
2
Drop on Production Line

User drops the card onto a Production Line drop zone. Frontend sends API request with SO ID, PL code, and company context.

frappe.call() → shangu_erp.api.create_line_allocation
3
Server-Side Validation

The whitelisted method performs 5 validation checks: (1) SO exists, (2) SO belongs to user's company, (3) PL exists, (4) PL belongs to user's company, (5) No duplicate allocation, (6) Capacity not exceeded.

frappe.db.exists(), frappe.has_permission(), frappe.db.get_value()
4
ORM Persistence

If all validations pass, a new Line Allocation document is created via Frappe ORM. The document goes through the full DocType lifecycle including validation triggers and naming series.

frappe.new_doc('Line Allocation').insert()
5
Response & UI Update

Success response returns the created allocation. Frontend refreshes production line capacity and allocation displays. On validation failure, the specific error type and message are returned.

JSON response with message or exc_type fields

Key Design Decisions

External Interface, Not a Frappe Page

The assignment explicitly requires an 'external planning interface' that integrates with ERPNext through its API framework. Building a standalone React SPA demonstrates proper API integration patterns rather than relying on Frappe's built-in page system.

Whitelisted Methods Over REST API

While ERPNext provides a generic REST API, using @frappe.whitelist() decorated methods allows us to encapsulate business logic, enforce validation rules, and maintain clean separation of concerns. Each method is a well-defined contract.

Server-Side Only Validation

All business rules (duplicate check, capacity check, permission check) are enforced exclusively on the server. The frontend may show warnings, but the server is the single source of truth. This prevents bypass via API calls or browser manipulation.

Company-Level Permission via User Permissions

ERPNext's User Permission system is used to restrict data access by company. The 'match' field in DocType permissions ensures that users can only see and modify data belonging to their assigned company. This is enforced at the ORM level, not just the UI.

Submittable DocType for Line Allocation

Making Line Allocation a submittable DocType (is_submittable: 1) enables the Draft → Submitted → Cancelled workflow. This provides audit trails, prevents accidental modifications, and aligns with ERPNext's document lifecycle patterns.

Unique Constraint on Sales Order Field

The 'unique: 1' property on the sales_order field in the Line Allocation DocType provides database-level duplicate prevention. Even if the application-level check fails, the database constraint ensures data integrity.

Migration Architecture (Task 2)

Import Strategy

Master Data First: Companies → Warehouses → Items → Accounts → Employees. This ensures all link references exist before transactional data is imported.
Batch Processing: Records imported in batches of 50 via the Data Import Tool to manage memory and provide progress tracking.
Validation Hooks: Each DocType's validate() method runs during import, ensuring business rules are enforced even during bulk operations.
Error Isolation: Failed records are logged and skipped without aborting the entire batch. This allows maximum data import while maintaining a clear error trail.

Reconciliation Methodology

Count Verification: Source record counts compared against ERPNext counts per DocType using frappe.db.count().
Financial Reconciliation: Trial balance verified by comparing sum of debits vs credits per company. GL entries cross-referenced with source transactions.
Stock Valuation: Imported stock values compared against source using weighted average method. Variances traced to specific failed entries.
Automated Reports: SQL queries generate reconciliation reports that can be run at any time to verify data integrity post-migration.