# MUNACHIMEX BMS â€” PROJECT STATE

Last updated: 2026-10-01
Owner/admin: admin / Admin123!

## ARCHITECTURE â€” Two Standalone Apps, Two Databases, Subdomains

| App | Folder | Database | Local URL | Production URL |
|---|---|---|---|---|
| Shop B | C:\xampp\htdocs\shop-b-bms | shopb_bms | http://shopb.local/ | https://shopb.yourdomain.com |
| Munachimex | C:\xampp\htdocs\munachimex-bms | munachimex_bms | http://munachimex.local/ | https://munachimex.yourdomain.com |

Shop B is the FULL app (includes Registers + ATC).
Munachimex is the SUBSET (no Registers, no ATC).

## LOCAL ENVIRONMENT SETUP

Hosts file (C:\Windows\System32\drivers\etc\hosts):
    127.0.0.1    shopb.local
    127.0.0.1    munachimex.local

Vhosts (C:\xampp\apache\conf\extra\httpd-vhosts.conf): two <VirtualHost> blocks.
Each DocumentRoot points at the app's /public folder.

.htaccess in each app's public/:  RewriteBase /  (was the redirect-loop bug fix)

## USERS (both databases)

| Username | Password | Role | role_id |
|---|---|---|---|
| admin | Admin123! | owner | 1 |
| john | Admin123! | cashier | 3 |

Roles: 1=owner, 2=manager, 3=cashier, 4=storekeeper, 5=fleet_officer

## PERMISSIONS SYSTEM

Permissions live in config/permissions.php (NOT the DB permissions table â€” that one is unused).
    owner = ['*']
    manager, cashier, storekeeper, fleet_officer â€” explicit lists
    Auth::can() / Auth::require() with wildcard support.

## FEATURES â€” CURRENT STATE

### Auth
- bcrypt (cost 10) via password_hash / password_verify
- Login, logout, forgot-password routes
- Session: MUNACHIMEX_SESS cookie, storage/sessions/ folder

### Inventory
- Products CRUD
- Product form: Opening Stock is EDITABLE (removed the disabled attribute)
- Editing Opening Stock logs a stock movement (opening_balance or adjustment)
- Stock Adjustment via Products -> History -> Adjust Stock
- NEW: Receive Stock modal (+ Receive button on each products row)
    - Route: inventory/product-receive
    - Controller: ProductController::receiveStock()
    - Permission: inventory.adjust
    - Adds quantity, logs a 'purchase' movement
- NEW: Stock Movements page
    - Route: inventory/movements
    - Controller: ProductController::movements()
    - Service: StockService::allMovements()
    - View: app/Views/products/movements.php
    - Sidebar link under Inventory
    - Filters: date range, movement type, product

### Sales
- Sales CRUD, PDF, print, confirm-delivery
- CREDIT LIMIT ENFORCEMENT (2026-09-30):
    - Method: Customer::checkCreditLimit($customerId, $additionalCredit)
    - Controller check: SaleController::store() blocks if new balance > limit
    - Formula: current_balance + (sale_total - amount_paid) > credit_limit
    - Skips if: customer_id empty, credit_limit <= 0, or fully paid
    - No admin override yet (hard block for everyone)

### Customers
- Full CRUD, account statement, debtors page
- credit_limit field (0 = unlimited)

### Purchases, Suppliers, Logistics, Finance, Staff, Registers, ATC
- All working as built

### Dashboard
- Welcome greeting now: 
    shop-b-bms:    "Welcome back, Munachimex Building Materials Global Ltd Shop B"
    munachimex:    "Welcome back, Munachimex Building Materials Global Ltd"
- Login flash: "Welcome back, {user_name}!"

## PRODUCT STOCK EDITS â€” THE RULE

When editing Opening Stock in the product form:
1. Compare old vs new
2. If different, insert a stock_movements row:
   - type 'opening_balance' if old was 0
   - type 'adjustment' otherwise
   - notes contain the before -> after values
   - staff_id = current user
3. Then write current_stock to products table

## STOCK_MOVEMENTS TABLE

Columns: id, movement_date, product_id, movement_type (enum),
         quantity, unit_cost, source_location_id, destination_location_id,
         reference_type, reference_id, staff_id, notes, created_at

Movement types: purchase, sale, return, transfer, adjustment, damage, opening_balance

## DEPLOYMENT KIT

Location: C:\Users\US\Desktop\bms-deploy-20261001_112634\
(or wherever you copied the backup)

Files:
- shop-b-bms-FINAL.zip          -> upload to hosting, extract to app folder
- munachimex-bms-FINAL.zip      -> upload to hosting, extract to app folder
- shop-b-bms-db.sql             -> import into MySQL database
- munachimex-bms-db.sql         -> import into MySQL database
- databases-20261001_112634.zip -> both dumps bundled
- shop-b-bms/                   -> folder version of the app
- munachimex-bms/               -> folder version of the app

Inside each app folder:
- env.production.example        -> copy to .env, fill in values
- DEPLOY-README.md              -> deployment steps

## DEPLOYMENT STEPS (when you buy hosting + domain)

1. Buy domain (e.g. munachimex.com)
2. Buy shared hosting with cPanel (recommend: WhoGoHost, QServers, Hostinger)
3. Point domain nameservers at hosting
4. In cPanel:
   a. Create subdomain shopb.munachimex.com -> docroot /home/account/shop-b-bms/public
   b. Create subdomain munachimex.munachimex.com -> docroot /home/account/munachimex-bms/public
   c. Create two MySQL databases + users
   d. Upload both zips via File Manager
   e. Extract into /home/account/shop-b-bms/ and /home/account/munachimex-bms/
   f. Import both .sql files into their respective databases
   g. Copy env.production.example -> .env in each app
   h. Edit .env with real DB credentials + real domain URLs
   i. Set storage/ and storage/sessions/ to 755 (writable)
   j. Install SSL via Let's Encrypt (usually 1 click)
5. Log in to each app with admin / Admin123!
6. CHANGE ADMIN PASSWORD immediately (Settings -> User Accounts)
7. Test: create a sale, receive stock, view stock movements, check credit limit block

## GOING FORWARD â€” RULES

1. NEVER use Set-Content -Encoding UTF8 (adds BOM to PHP files)
   Use: [System.IO.File]::WriteAllText($path, $content, (New-Object System.Text.UTF8Encoding $false))

2. On Windows, normalize paths with str_replace('\\', '/', dirname(...)) before URL use.

3. In .htaccess under a vhost, RewriteBase must be /.

4. Prefer ASCII / HTML entities in code:
   &mdash;  &lsaquo;  &rsaquo;  &#8358;  &hellip;  &#10003;  &#10007;

5. When creating zips in PowerShell 5.1, use:
   [System.IO.Compression.ZipFile]::CreateFromDirectory($src, $zip)
   NOT Compress-Archive (it drops subfolders silently).

6. When verifying zips, remember PowerShell may store paths with backslash:
   - Use -replace '\\','/' if matching against forward-slash paths

7. When editing PowerShell scripts that insert into PHP, note ONE-SHOT vs IDEMPOTENT
   and check for existing occurrences to avoid duplicates.

## DIAGNOSTIC COMMANDS

### Encoding corruption scan:
Get-ChildItem "C:\xampp\htdocs" -Recurse -Filter *.php |
    Where-Object { $_.FullName -notmatch '\\vendor\\|\\node_modules\\' } |
    ForEach-Object {
        $b = [System.IO.File]::ReadAllBytes($_.FullName)
        $n = 0
        for ($i = 0; $i -lt $b.Length - 1; $i++) {
            if (($b[$i] -eq 0xC3 -and $b[$i+1] -eq 0xA2) -or
                ($b[$i] -eq 0xC3 -and $b[$i+1] -eq 0x83) -or
                ($b[$i] -eq 0xC2 -and $b[$i+1] -eq 0xA2) -or
                ($b[$i] -eq 0xC3 -and $b[$i+1] -eq 0xB0)) { $n++ }
        }
        if ($n -gt 0) { "{0,5}  {1}" -f $n, $_.FullName }
    }

### MySQL (XAMPP):
& "C:\xampp\mysql\bin\mysql.exe" -u root -e "SHOW DATABASES;"
& "C:\xampp\mysql\bin\mysql.exe" -u root -B -e "SELECT id, name, current_stock FROM shopb_bms.products LIMIT 10;"
& "C:\xampp\mysql\bin\mysql.exe" -u root -B -e "SELECT * FROM shopb_bms.stock_movements ORDER BY id DESC LIMIT 10;"
& "C:\xampp\mysql\bin\mysql.exe" -u root -B -e "SELECT id, customer_name, credit_limit, current_balance FROM shopb_bms.customers;"

### Restart Apache:
Stop-Process -Name httpd -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
Start-Process "C:\xampp\apache\bin\httpd.exe"

### Apache error log (per subdomain):
Get-Content "C:\xampp\apache\logs\shopb.local-error.log" -Tail 30
Get-Content "C:\xampp\apache\logs\munachimex.local-error.log" -Tail 30

## KEY FILE LOCATIONS (both apps, same structure)

- Route map:              public/index.php
- DB config:              config/database.php (reads from .env)
- Permissions:            config/permissions.php
- Constants:              config/constants.php
- Bootstrap:              app/bootstrap.php
- .env:                   .env (at project root)
- Core:                   app/Core/{Database,Auth,Audit,Env,Helpers,Money}.php
- Controllers:            app/Controllers/*.php
- Models:                 app/Models/*.php
- Services:               app/Services/*.php
- Views:                  app/Views/<section>/*.php
- Dashboard view:         public/dashboard/index.php
- Layout (sidebar):       app/Views/layouts/app.php
- Auth layout:            app/Views/layouts/auth.php

## KNOWN ISSUES / TODO

- No admin override for credit limit (hard block only)
- Stock Movements page has no CSV export yet
- Sales/Purchases transaction history pages not built (only stock movements)
- Reports/Settings subpages exist only in shop-b-bms (not expanded in munachimex)
- Session MUNACHIMEX_SESS fixed name â€” fine for now

## WHEN THIS CHAT TIMES OUT

1. Open a new chat.
2. Say: "Continuing Munachimex BMS. Here is the project state:"
3. Paste this entire file.
4. Then describe the task you want to do next.