You are an expert backend engineer and accountant.
I want you to build the Purchases + Inventory + Finance (auto journal entries) module for a beauty salon ERP.

## Tech stack
- Use Node.js + Express.
- Use SQLite as the database (file-based).
- Use a clean project structure: src/, src/models/, src/routes/, src/controllers/, src/services/.
- Use async/await and modern JavaScript.

I will start with this module only, we will extend later.

########################################
# 1) SUPPLIERS MODULE (الموردين)
########################################

Create database tables and REST APIs to manage suppliers.

### Table: suppliers
Fields:
- id (integer, primary key, auto increment)
- name_ar (string, not null)                // اسم المورد
- cr_number (string, nullable)              // رقم السجل التجاري
- vat_number (string, nullable)             // الرقم الضريبي
- city_ar (string, nullable)                // المدينة
- supplier_type (string, nullable)          // نوع المورد (مثلاً: منتجات، خدمات، كافتيريا...)
- image_url (string, nullable)              // رابط صورة / مرفق للمورد
- representative_name (string, nullable)    // اسم مندوب المورد
- representative_phone (string, nullable)   // رقم جوال المندوب
- is_active (boolean, default true)
- created_at (datetime)
- updated_at (datetime)

### Table: supplier_bank_accounts
A supplier can have multiple bank accounts.
Fields:
- id (integer, primary key, auto increment)
- supplier_id (foreign key to suppliers.id)
- bank_name_ar (string, nullable)
- iban (string, nullable)
- account_number (string, nullable)
- is_default (boolean, default false)

### Supplier APIs
1) GET /suppliers
   - List suppliers.

2) POST /suppliers
   - Create a new supplier with all fields above, and optional bank accounts array.
   - Request JSON example:
   {
     "name_ar": "مورد أ",
     "cr_number": "1234567890",
     "vat_number": "300123456700003",
     "city_ar": "جدة",
     "supplier_type": "منتجات تجميل",
     "image_url": null,
     "representative_name": "أحمد علي",
     "representative_phone": "0550000000",
     "bank_accounts": [
       {
         "bank_name_ar": "بنك الراجحي",
         "iban": "SA0000000000000000000000",
         "account_number": "1234567890",
         "is_default": true
       }
     ]
   }

3) GET /suppliers/:id
   - Get supplier with its bank accounts.

4) PUT /suppliers/:id
   - Update supplier.

5) DELETE /suppliers/:id
   - Soft delete (set is_active = false).

Also, when creating a purchase invoice (below), I want the ability to:
- Select an existing supplier OR create a new one inline in the same request.

########################################
# 2) UNITS & PRODUCTS & INVENTORY (المخزون)
########################################

### Table: units
To define purchase/sell units (كرتون، حبة، عبوة، شدة...).
Fields:
- id (integer, primary key, auto increment)
- name_ar (string, not null)    // مثال: "كرتون", "حبة", "عبوة", "شدة"
- code (string, nullable)       // optional short code
- created_at (datetime)
- updated_at (datetime)

Seed some default units:
- "كرتون"
- "حبة"
- "عبوة"
- "شدة"

### Table: products
Fields:
- id (integer, primary key, auto increment)
- name_ar (string, not null)             // اسم الصنف
- sku (string, nullable, unique)         // كود داخلي اختياري
- purchase_unit_id (foreign key to units.id)  // وحدة الشراء (كرتون مثلاً)
- sell_unit_id (foreign key to units.id)      // وحدة البيع (حبة مثلاً)
- conversion_factor (real, not null, default 1) // عدد وحدات البيع في وحدة الشراء (مثلاً: 1 كرتون = 12 حبة)
- default_purchase_price (real, nullable)      // سعر شراء افتراضي
- default_sell_price (real, nullable)          // سعر بيع افتراضي
- has_expiry_date (boolean, default false)
- is_active (boolean, default true)
- created_at (datetime)
- updated_at (datetime)

### Table: stock_batches
Because of expiry dates, track stock by batch.
Fields:
- id (integer, primary key, auto increment)
- product_id (foreign key to products.id)
- expiry_date (date, nullable)         // تاريخ الانتهاء (اختياري)
- quantity_on_hand (real, not null)    // الكمية الحالية بوحدة البيع
- unit_cost (real, not null)           // تكلفة الوحدة (وحدة البيع)
- created_at (datetime)
- updated_at (datetime)

### Table: stock_movements
General stock movement log.
Fields:
- id (integer, primary key, auto increment)
- product_id (foreign key to products.id)
- movement_date (date)
- movement_type (string)  // "purchase", "sale", "consume", "damage", "adjustment"
- quantity_in (real, default 0)
- quantity_out (real, default 0)
- unit_cost (real, nullable)
- reference_type (string, nullable)    // "PurchaseInvoice", "ManualAdjustment", ...
- reference_id (integer, nullable)
- batch_id (integer, nullable, foreign key to stock_batches.id)
- created_at (datetime)

### Product APIs
- CRUD for units: GET/POST/PUT/DELETE /units
- CRUD for products: GET/POST/PUT/DELETE /products

When defining/creating a product:
- purchase_unit_id: reference to units.
- sell_unit_id: reference to units.
- conversion_factor: how many sell units in one purchase unit.
- default_purchase_price, default_sell_price, has_expiry_date.

########################################
# 3) PAYMENT METHODS & FINANCE BASICS (طرق الدفع)
########################################

### Table: payment_methods
This is a finance model to define payment methods and later link them to accounts.
Fields:
- id (integer, primary key, auto increment)
- name_ar (string, not null)          // مثال: "كاش", "تحويل بنكي", "بطاقة"
- code (string, nullable)
- type (string, nullable)             // "cash", "bank", "card"
- is_active (boolean, default true)
- created_at (datetime)
- updated_at (datetime)

Seed:
- "كاش" (code "CASH", type "cash")
- "تحويل بنكي" (code "BANK", type "bank")
- "بطاقة" (code "CARD", type "card")

We will later link these to accounting accounts. For now we just need the model.

APIs:
- CRUD: /payment-methods

########################################
# 4) PURCHASE INVOICES (فاتورة المشتريات)
########################################

We need to be able to:
- Choose an existing supplier OR create a new one inline.
- Choose a representative (مندوب المورد) (from the supplier record).
- Choose an existing product OR create a new product inline.
- Handle purchase unit vs sell unit vs conversion factor.
- Handle tax type per line: شامل / غير شامل / معفي.
- Support payment methods: cash, bank, card, partial payments.
- Automatically create inventory movements.
- Automatically create journal entries for accounting.

### Table: purchase_invoices
Fields:
- id (integer, primary key, auto increment)
- supplier_id (foreign key to suppliers.id)
- supplier_representative_name (string, nullable)  // if needed, taken from supplier or entered manual
- invoice_date (date, not null)
- due_date (date, nullable)
- payment_status (string)   // "unpaid", "partially_paid", "paid"
- subtotal_amount (real, not null, default 0)    // مجموع بدون ضريبة
- vat_amount (real, not null, default 0)         // مجموع الضريبة
- total_amount (real, not null, default 0)       // الإجمالي مع الضريبة
- notes (string, nullable)
- created_at (datetime)
- updated_at (datetime)

### Table: purchase_invoice_lines
Fields:
- id (integer, primary key, auto increment)
- purchase_invoice_id (foreign key to purchase_invoices.id)
- product_id (foreign key to products.id)
- purchase_unit_id (foreign key to units.id)
- sell_unit_id (foreign key to units.id)
- quantity_purchase_unit (real, not null)     // الكمية بوحدة الشراء (مثلاً 2 كرتون)
- conversion_factor (real, not null)         // عدد وحدات البيع في وحدة الشراء
- quantity_sell_unit (real, not null)        // الكمية بوحدة البيع = quantity_purchase_unit * conversion_factor
- purchase_price_per_purchase_unit (real, not null)
- purchase_price_per_sell_unit (real, not null)  // محسوبة تلقائياً = purchase_price_per_purchase_unit / conversion_factor
- vat_type (string, not null)      // "inclusive" (شامل)، "exclusive" (غير شامل), "exempt" (معفي)
- vat_rate (real, not null)        // e.g. 0.15
- vat_amount (real, not null)
- line_subtotal (real, not null)   // بدون ضريبة
- line_total (real, not null)      // مع الضريبة
- expiry_date (date, nullable)     // اختياري

### Table: purchase_payments
To handle partial payments (جزء كاش وجزء تحويل بنكي …).
Fields:
- id (integer, primary key, auto increment)
- purchase_invoice_id (foreign key to purchase_invoices.id, nullable)  // can be null if payment is on account
- supplier_id (foreign key to suppliers.id)
- payment_method_id (foreign key to payment_methods.id)
- payment_date (date, not null)
- amount (real, not null)
- notes (string, nullable)
- created_at (datetime)
- updated_at (datetime)

### Purchase Invoice API

#### POST /purchase-invoices
Creates a new purchase invoice, optionally creating supplier and products on the fly.

Request JSON example:

{
  "supplier": {
    "id": null,
    "name_ar": "مورد أ",
    "cr_number": "1234567890",
    "vat_number": "300123456700003",
    "city_ar": "جدة",
    "supplier_type": "منتجات تجميل",
    "image_url": null,
    "representative_name": "أحمد علي",
    "representative_phone": "0550000000"
  },
  "invoice_date": "2025-10-01",
  "due_date": "2025-11-01",
  "lines": [
    {
      "product": {
        "id": null,
        "name_ar": "شامبو 500 مل",
        "purchase_unit_name_ar": "كرتون",
        "sell_unit_name_ar": "حبة",
        "conversion_factor": 12,
        "has_expiry_date": true,
        "default_sell_price": 30
      },
      "quantity_purchase_unit": 1,
      "purchase_price_per_purchase_unit": 360,   // سعر شراء الكرتون
      "vat_type": "inclusive",                   // "inclusive", "exclusive", "exempt"
      "vat_rate": 0.15,
      "expiry_date": "2026-01-31"
    }
  ],
  "notes": "فاتورة شراء من مورد أ"
}

Behavior:
- If supplier.id is null, create a new supplier with the given data.
- If product.id is null, create a new product:
  - Create or find purchase_unit by purchase_unit_name_ar.
  - Create or find sell_unit by sell_unit_name_ar.
  - Set conversion_factor.
  - Set default_purchase_price and default_sell_price.
- For each line:
  - Calculate quantity_sell_unit = quantity_purchase_unit * conversion_factor.
  - If vat_type = "inclusive":
    - line_total = purchase_price_per_purchase_unit * quantity_purchase_unit
    - line_subtotal = line_total / (1 + vat_rate)
    - vat_amount = line_total - line_subtotal
  - If vat_type = "exclusive":
    - line_subtotal = purchase_price_per_purchase_unit * quantity_purchase_unit
    - vat_amount = line_subtotal * vat_rate
    - line_total = line_subtotal + vat_amount
  - If vat_type = "exempt":
    - vat_amount = 0
    - line_subtotal = purchase_price_per_purchase_unit * quantity_purchase_unit
    - line_total = line_subtotal
  - purchase_price_per_sell_unit = line_subtotal / quantity_sell_unit
- Sum totals:
  - subtotal_amount = sum(line_subtotal)
  - vat_amount = sum(vat_amount)
  - total_amount = sum(line_total)
- Insert a stock batch for this product (stock_batches) with:
  - product_id, expiry_date, quantity_on_hand (quantity_sell_unit), unit_cost (purchase_price_per_sell_unit)
- Insert a stock_movement record (type "purchase") linking the batch.

Return the created invoice + lines.

Update purchase_invoices.payment_status based on total of payments:
- If no payments: "unpaid"
- If payments sum < total_amount: "partially_paid"
- If payments sum == total_amount: "paid"

#### POST /purchase-invoices/:id/payments
Register a payment for a purchase invoice.

Request JSON example (partial cash payment):

{
  "payment_date": "2025-10-15",
  "payment_method_id": 1,      // e.g. CASH
  "amount": 500,
  "notes": "دفع جزء كاش"
}

Then another payment:

{
  "payment_date": "2025-11-20",
  "payment_method_id": 2,      // e.g. BANK TRANSFER
  "amount": 650,
  "notes": "تحويل بنكي"
}

Behavior:
- Create a purchase_payments record.
- Recalculate the remaining balance.
- Update payment_status accordingly.

########################################
# 5) BASIC ACCOUNTING / AUTO JOURNAL ENTRIES (قيود مالية تلقائية)
########################################

Assume we have or will have an accounts table in the future.
For now, hard-code or configure the following account codes in a simple config object:

- supplier_control_account_code = "2100"          // الموردين
- vat_input_account_code = "2410"               // ضريبة مدخلات
- inventory_account_code = "1201"               // مخزون منتجات
- cash_account_code = "1110"                    // النقدية
- bank_account_code = "1120"                    // البنك

### Tables:

#### journal_entries
- id (integer, primary key, auto increment)
- entry_number (string, unique)  // e.g. "JV-0001"
- date (date, not null)
- description (string, nullable)
- created_at (datetime)
- updated_at (datetime)

#### journal_lines
- id (integer, primary key, auto increment)
- journal_entry_id (foreign key to journal_entries.id)
- account_code (string, not null)
- debit (real, not null, default 0)
- credit (real, not null, default 0)

Business rule: sum(debit) must equal sum(credit) for each journal_entry.

### Automatic entries from purchase invoice:

When a purchase invoice is created and posted (total_amount = 1150 with vat 150 and net inventory 1000):

Example (total_amount = 1150, vat_amount = 150, subtotal_amount = 1000):

Create a journal_entry with:
- date = invoice_date
- description = "قيد فاتورة مشتريات رقم [id] للمورد [supplier name]"

Journal lines:
- Debit inventory_account_code with subtotal_amount    // المخزون 1000 مدين
- Debit vat_input_account_code with vat_amount         // ضريبة مدخلات 150 مدين
- Credit supplier_control_account_code with total_amount // المورد دائن 1150

So the entry is:

Dr Inventory 1000
Dr VAT Input 150
   Cr Supplier 1150

### Automatic entries from supplier payments:

When registering a payment for an invoice for supplier "مورد أ":

Case 1: Payment method is CASH (كاش)
- Example: 500 on 15-10-2025

Journal:
- date = payment_date
- description = "سداد للمورد [supplier name] (كاش)"

Lines:
- Debit supplier_control_account_code 500   // المورد أ مدين 500
- Credit cash_account_code 500             // النقدية دائن 500

Case 2: Payment method is BANK (تحويل بنكي)
- Example: 650 on 20-11-2025

Journal:
- Debit supplier_control_account_code 650   // المورد أ مدين 650
- Credit bank_account_code 650             // البنك دائن 650

If payment method is CARD (بطاقة), we can still treat it as bank_account_code or a separate account code if needed later.

### APIs for testing:

- GET /purchase-invoices/:id
  - Return invoice, lines, payments and related journal entries.

- GET /suppliers/:id/balance
  - Calculate supplier balance:
    - Start from all journal_lines where account_code = supplier_control_account_code AND related to this supplier.
    - Balance = total credit - total debit (or debit - credit, depending on convention), but please implement clearly as:
      - supplier_balance = sum(credits) - sum(debits)
      - Positive => we owe the supplier, negative => supplier has debit balance.

########################################
# 6) INVENTORY REPORTS (تقارير المخزون)
########################################

Create simple inventory report endpoints:

1) GET /reports/inventory-summary
   - For each product:
     - product_id, name_ar
     - quantity_on_hand (sum of stock_batches.quantity_on_hand)
     - total_cost (sum of stock_batches.quantity_on_hand * unit_cost)
   - Return as JSON list.

2) GET /reports/inventory-movements?product_id=X
   - Return stock_movements for that product ordered by date:
     - date, movement_type, quantity_in, quantity_out, unit_cost, reference_type, reference_id
   - This is like stock card (كرت الصنف).

########################################
# GENERAL REQUIREMENTS
########################################

- Use Arabic field labels in API JSON where appropriate (e.g., name_ar).
- Add basic validation and error messages in Arabic when possible (e.g., "المورد غير موجود", "المنتج غير موجود", "إجمالي المدين لا يساوي إجمالي الدائن").
- Add a simple README.md in Arabic explaining:
  - جداول النظام (الموردين، المنتجات، وحدات القياس، فواتير المشتريات، حركات المخزون، طرق الدفع، القيود اليومية)
  - مثال عملي لفاتورة مشتريات 1150 ريال (1000 مخزون + 150 ضريبة) وقيدها المالي
  - مثال لسداد جزئي (500 كاش + 650 تحويل بنكي) وربطها بالقيود المالية.
- After building the system, run the server and show me example curl commands or HTTP requests to:
  - إنشاء مورد جديد
  - إنشاء وحدة جديدة
  - إنشاء منتج جديد
  - إنشاء فاتورة مشتريات مع تعريف مورد وصنف جديدين
  - تسجيل دفعة كاش للمورد
  - تسجيل دفعة تحويل بنكي للمورد
  - استرجاع تقرير المخزون
  - استرجاع قيد اليومية الناتج عن عملية الشراء والدفع.
