# Binzagr PoV — Simulated & inferred data (03_Simulated)

**Version 3 — 22 Oct 2026.** Version 2 rebuilt the model on Binzagr's own mappings (received from Badr on 22 Oct): which activities are billed, who is behind each badge (payroll number and role, month by month), and units of sale per case / pallet. Version 3 adds the **head count from employee ids** (Mehul's suggestion): people are paid on every working day they are employed, not only on days they scan. §0 shows what moved.

**What this folder is:** the cleaned data (`02_Cleaned`) plus every gap filled by **research or a stated model**, so the PoV can be built and demonstrated now. Every table has a `basis` column and every number traces back to either the client's data or a cited source:

| Label | Meaning |
|---|---|
| **Actual** | Straight from `02_Cleaned` (client data, including Binzagr's mappings) |
| **Inferred** | Derived from client data by a stated rule / model — no outside numbers |
| **Researched** | A published fact or figure, cited in `sources.csv` (40 sources) |
| **Client** | A figure Binzagr stated in a meeting (e.g. example case rates), used only as a check |
| **Simulated** | Computed from Researched / assumed parameters, always with low / base / high |

**Validation:** 22 of 24 checks pass, 2 information warnings (`SIMULATION_CHECKS.md`; §5.7 explains the two warnings).

---

## 0. What changed (version 1 → 2 → 3)

| Figure | v1 (21 Oct) | v2 (22 Oct, mappings) | v3 (22 Oct, head count) | Why it moved |
|---|---|---|---|---|
| Recoverable labour, SAR / year (base) | 657,107 | 639,485 | 639,485 | v2: people counted once (payroll), standards by line size, role wages, batch posting not benchmarked; v3: unchanged (savings come from worked shifts) |
| Likely range (Monte Carlo P10–P90) | 512,994 – 832,086 | 491,647 – 805,806 | 491,647 – 805,806 |  |
| Recoverable hours / year (FTE) | 27,018 (11.0) | 26,082 (10.7) | 26,082 (10.7) |  |
| People paid (FTE) | 117 | 115 | 161 | v3: head count from employee ids - payroll-confirmed people paid on every working day from first to last scan |
| Simulated labour cost, SAR / year | 7,026,980 | 6,945,349 | 9,220,175 | v3: + paid days without a single scan |
| Recoverable as % of labour cost | 9.4% | 9.2% | 6.9% |  |
| Paid time with no scan | 46.9% | 46.0% | 58.9% | v3: unscanned part of worked days + whole paid days with no scan |
| Simulated EBIT after savings | 10.9% | 10.8% | 9.7% | baseline 6.5% by construction in every version |
| Labour per loose case picked, SAR | - | 0.77 | 0.77 worked days / 1.02 all paid days |  |
| Cost-recovery rate: case pick / pallet received | - | 1.64 / 25.87 | 2.17 / 34.35 | rates recover the larger labour cost |
| Billed activities | 8 services incl. loading, LP audit, QC | 9 Revenue Generation activities | same | loading, audit and QC are cost per Binzagr |

The savings figure holds: it comes from how Binzagr's own best shifts perform, and the head count does not change that. What the head count changes is the size of the bill those savings come out of - and the share of paid time nobody can see.

---

## 1. Headline results (Jeddah warehouse labour PoV)

| | Base | Monte Carlo P10 – P90 |
|---|---|---|
| Head count (average month) | 202 employee ids scan; 138 of them are payroll-confirmed people; Binzagr's user list shows 269 active people at these sites | |
| People paid (FTE, head-count basis) | **161** (scan days only: 115; every id salaried: 199) | |
| Simulated labour cost (annualised, head-count basis) | SAR 9,220,175 | SAR 6,838,024 – 11,415,787 |
| **Recoverable labour cost per year** | **SAR 639,485** (6.9%) | SAR 491,647 – 805,806 (5.1% – 9.9%) |
| Recoverable hours per year | 26,082 h (≈ 10.7 FTE) | 20,267 – 32,591 h |
| Labour per loose case picked / per pallet picked / per pallet received — days people worked | SAR 0.77 / 5.70 / 12.10 | (compare with client prices, V12) |
| Same, all paid days (head-count basis) | SAR 1.02 / 7.57 / 16.06 | (compare with Binzagr's facility cost per case / pallet, V11) |
| Outbound labour per case shipped (loose + in full pallets) | SAR 0.38 worked days / 0.50 all paid days | (60% of cases leave in full pallets) |

**How to say it to Binzagr:** *"If every full shift performed like the top quartile of your own shifts in the same role already does, Jeddah would need ~11 fewer FTE — about SAR 639k a year (range SAR 492k–806k)."* Deliberately conservative (§5.5); below the 15–35% gains vendors report from engineered standards ([S23]).

**The bigger finding — paid time nobody can see:** of 245,194 paid hours on the head-count basis (Feb–Sep), **58.9% have no scan** (50% at 2100-04, 64%–87% at the other sites). Two parts: the unscanned part of the days people did scan (46.0% of those days' paid hours), and **7,653 paid working days on which a payroll-confirmed person did not scan at all** (they scanned on 73% of the days they were expected to work, after leave and holidays). No savings are claimed on any of it: it can be leave, sickness, unscanned work, a colleague's badge, or idle time. Attendance by payroll number tells them apart.

**Where labour goes (task-based posting, §5.6):** 10.3% of labour cost is *In House* work no client is billed for (moves, replenishment, cycle counts); **14.5% goes on inbound paperwork** (shipment audit, reconciliation, closing POs).

**Prices vs cost:** on the days people worked, case picking costs **SAR 0.77 of simulated labour per loose case** (outbound labour incl. loading and its share of overhead, split between case and pallet picks by billed lines — the split matches actual pick time, 80.4% vs 80.1%). That is at the top of the SAR 0.50–0.80 per case Badr quoted for the same activity at different clients. Adding the paid days without a scan, it is **SAR 1.02** — above every price he quoted. Either part of that head count is not paid by these sites (or not full time), or case picking is priced below its labour and storage / other fees carry the margin. Binzagr's facility cost per case settles it (04_Gaps V11, V12). A full cost-recovery rate would be SAR 2.17 per case, so other charges (storage, shipped-HU and VAS fees) must carry much of Binzagr's revenue — only the real rate card can show it.

**Caution on the P&L:** the baseline P&L earns the benchmark 6.5% margin **by construction** (cost-recovery rate card, §5.7–5.8). Only the *change* is a result. Labour-to-revenue in this simulation (46.8% → 43.5%) is an artefact of the labour-share parameter, not evidence for or against the "25% → 18%" example — present it only once the actual P&L is in.

---

## 2. Folder contents

```
03_Simulated/
  data/ + csv/                         (same tables; CSV gzipped when large)
    sources                            40 sources (Researched + Client statements)
    sim_parameters                     41 parameters: low / base / high, basis, sources, notes
    sim_labour_cost_model              loaded monthly cost per role tier (operative, storekeeper, supervisory), shift & hour rates
    sim_standard_times                 standard seconds per WMS activity x line-size band (3 variants x 3 idle caps)
    inf_jeddah_headcount               per month: active people in Binzagr's user list, employee ids scanning, FTE / paid hours / labour cost on 3 bases
    sim_person_month_labour            one row per person (or unconfirmed login) x month: employed days, scan days, paid days without a scan, cost
    sim_session_attendance             23,589 PERSON work sessions: paid shifts / hours, observed vs unobserved time, earned hours,
                                       productivity, role, WMS role, cost tier, target, recoverable hours & SAR
    sim_wh_month_labour                warehouse x month roll-up
    sim_activity_volumes               wh x month x activity x client type: events, cases, pallets, hours, posted labour cost (Binzagr classes)
    sim_cost_per_unit_wh_month         labour cost per loose case, case shipped, pallet picked and pallet received by site and month
    sim_rate_card                      cost-recovery rate per billed activity (Binzagr revenue codes), low/base/high + external checks
    sim_jeddah_pnl_monthly             monthly simulated P&L: revenue, labour, other opex, EBIT, optimised EBIT, FTE
    sim_jeddah_pnl_by_wh_month         same by warehouse
    sim_scenarios                      Conservative / Base / Stretch
    sim_monte_carlo_draws / _summary   5,000 draws over all uncertain inputs; P10/P50/P90
    sim_opportunity_levers             five levers (Jeddah labour + four from Riyadh sales), low / base / high
    inf_client_principal_map           client code -> principals behind it; Binzagr BU vs external 3PL
    inf_warehouse_profile              site timelines, structural changes, client mix, cold-chain indicator
    inf_badge_session_risk             badge-sharing risk per person session (rules + IsolationForest)
    inf_customer_channel_map           customer hierarchy codes -> inferred channel (EN + AR name keywords)
    inf_outlet_activity                recency, visit cadence, churn proxy per customer
    inf_salesman_route_profile         drops/day, weekday pattern, revenue per drop per van rep
    sim_receivables_open_items         outstanding balances allocated to invoices (FIFO), due dates, days past due
    sim_receivables_customer_ageing    ageing buckets per credit customer
    ref_calendar_events                2026 Hijri calendar + cited events/holidays + observed activity index
  scripts/   SIMULATION_CHECKS.md   simulation_checks.json   MANIFEST.csv
```

---

## 3. Research — sources

| link | title | publisher | date |
|---|---|---|---|
| [S01](https://gulfmigration.grc.net/saudi-arabia-average-monthly-wages-per-paid-employee-in-main-employment-by-nationality-saudi-non-saudi-sex-and-education-level-in-sr-q4-2025/) | GASTAT Labour Force Survey Q4 2025 - average monthly wages by nationality, sex, education (via GLMM) | General Authority for Statistics / Gulf Labour Markets & Migration | 2026 |
| [S02](https://beoe.gov.pk/foreign-jobs/3185707) | Job posting: labour, Jeddah - SAR 1,800-2,000/month + free accommodation, transport, medical, air ticket; SAR 200 food allowance | Bureau of Emigration & Overseas Employment, Govt of Pakistan | offer expiry 17/04/2025 |
| [S03](https://beoe.gov.pk/foreign-jobs/4093177/4576207/19-warehouse-workers-required-in-saudi-arabia-salary-1200-1400-rial-sar) | Job posting: 19 warehouse workers, Riyadh - SAR 1,200-1,400 | Bureau of Emigration & Overseas Employment, Govt of Pakistan | 2026 |
| [S04](https://noqta.tn/en/blog/gosi-contribution-rate-salary-deduction-saudi-2026) | GOSI contribution rates 2026 (old/new scheme; non-Saudi 2% occupational hazards; base = basic + housing) | Noqta (summary of Social Insurance Law) | 2026 |
| [S05](https://payspace.com/blog/saudi-arabia-gosi-contribution-rates-increase-1-july-2026-new-employee-employer-rates) | GOSI rates increase 1 July 2026 (+0.5% pension each side, new-scheme Saudis) | Deel Local Payroll (PaySpace) | 2026-07-02 |
| [S06](https://etqanlawfirm-sa.com/en/labour-iqama-fees-in-saudi-arabia/) | Expat levy ~SAR 800/worker/month; iqama SAR 650/12 months; employer pays | Etqan Law Firm | 2025-07-17 |
| [S07](https://www.qoyod.com/en/learn/working-hours-weekly-rest-saudi-labor-law/) | Saudi Labor Law working hours: Art. 98 (8h/day or 48h/week; Ramadan 6h/36h for Muslims), Art. 101 (30-min rest after 5h; max 12h at workplace), Art. 104 (Friday rest) | Qoyod (guide to the Labor Law) | 2026 |
| [S08](https://noqta.tn/en/blog/overtime-pay-calculation-saudi-labor-law-2026) | Overtime Art. 107: hourly wage + 50% of basic; hourly basic = basic/240; 720 h/yr cap | Noqta | 2026-09-07 |
| [S09](https://www.teamed.global/gulf-employment-law/saudi-arabia) | End-of-service award (Art. 84: half month per year for first 5 years); annual leave 21 days (Art. 109) | Teamed | 2025 |
| [S10](https://www.qoyod.com/en/glossary/housing-allowance/) | Housing allowance usually 25% of basic; included in GOSI base and end-of-service base | Qoyod Business Glossary | 2026-10-12 |
| [S11](https://www.meinsurancereview.com/News/ViewNewsLetterArticle/id/92996/Type/MiddleEast/Saudi-Arabia-Health-insurers-rely-on-investment-gains-for-profits-despite-sharp-premium-hikes) | Average health-insurance premium per insured person SAR 4,511 (end 2024; Argaam study) | Middle East Insurance Review | 2025-09-24 |
| [S12](https://www.argaam.com/en/article/articledetail/id/1460333) | Nitaqat: Saudi employee counts fully from SAR 4,000/month, half at SAR 3,000 | Argaam | 2021-04-18 |
| [S13](https://www.scdigest.com/ontarget/13-07-17-1.php?cid=7230) | PF&D allowances: fatigue 3-5%, personal ~5%, delay 2-4% | Supply Chain Digest (quoting Manhattan Associates, enVista) | 2013-07-17 |
| [S14](https://doi.org/10.1016/j.ejor.2006.07.009) | de Koster, Le-Duc & Roodbergen (2007) Design and control of warehouse order picking - order picking up to 55% of warehouse operating expense; travel ~50% of picker time | European Journal of Operational Research 182(2):481-501 | 2007 |
| [S15](https://www.mhlnews.com/labor-management/article/22039898/labor-productivity-can-be-50-of-warehouse-operating-cost) | Labour can account for up to 50% of a warehouse total operating cost (Infor) | MHL News | 2009-07-15 |
| [S16](https://group.dhl.com/en/media-relations/press-releases/2026/dhl-group-annual-earnings-2025.html) | DHL Supply Chain FY2025: revenue EUR 17,778m, EBIT EUR 1,161m, EBIT margin 6.5% | DHL Group annual earnings 2025 | 2026-03-05 |
| [S17](https://fulfill.com/3pl-pricing) | 3PL pricing benchmarks (Fulfillment Advisor 2025 survey, 600+ warehouses): receiving USD 10.52/pallet, receiving labour USD 39.88/h, storage USD 20.17/pallet/month, B2B order USD 4.80 | Fulfill.com | 2026-06-10 |
| [S18](https://redstagfulfillment.com/freight-rates-and-fuel-surcharges/) | 3PL pricing guide citing Warehousing & Fulfillment 2025 survey (receiving USD 5-15/pallet, storage USD 15-40/pallet/month) | Red Stag Fulfillment | 2025 |
| [S19](https://www.averxglobal.com/blog/warehouse-storage-solutions-riyadh) | Riyadh storage SAR 80-250 per pallet/month; industrial SAR 15-45/sqm/month | Averx Global | 2026-06-18 |
| [S20](https://uaefreezonefinder.com/uae-fulfillment-warehousing-3pl-guide-2026/) | UAE 3PL: storage AED 50-120/pallet/month, inbound receiving AED 3-8 per carton, pick & pack AED 8-20/order | UAE Free Zone Finder | 2026-08-11 |
| [S21](https://www.arabianbusiness.com/industries/real-estate/saudi-logistics-rents-surge-to-record-highs-as-tech-and-retail-giants-snap-up-scarce-space-report) | Saudi industrial & logistics rents: Riyadh SAR 208/sqm (+16%), Dammam SAR 231/sqm (+9%), Jeddah +8% (Knight Frank Autumn 2025) | Arabian Business | 2025 |
| [S22](https://www.foodbusinessmea.com/?p=2162850) | JLL KSA Industrial Q2 2026: Jeddah rents +4.8%; top submarket SAR 300/sqm/yr | Food Business MEA | 2026-08-26 |
| [S23](https://www.easymetrics.com/understanding-engineered-labor-standards) | Engineered labour standards typically raise productivity 15-35% (vendor experience) | Easy Metrics | 2025-04-25 |
| [S24](https://blog.synerion.com/buddy-punching-and-employee-time-theft-statistics) | Buddy punching accounts for 2.2% of gross payroll (Nucleus Research); 19% of employees admit to it | Synerion | 2015-01-16 |
| [S25](https://gulfnews.com/world/gulf/saudi/saudi-arabia-announces-eid-al-fitr-holidays-for-private-and-non-profit-sectors-1.500462986) | Eid al-Fitr 2026 private-sector holiday: 4 days from close of business 29 Ramadan (Wed 18 Mar) | Gulf News | 2026-03-04 |
| [S26](https://gulfnews.com/world/gulf/saudi/saudi-arabia-announces-first-day-of-eid-al-fitr-2026-1.500478696) | Eid al-Fitr 2026 = Friday 20 March (Shawwal crescent not sighted) | Gulf News | 2026-03-18 |
| [S27](https://petra.gov.jo/cgi-bin/index.php/en/news/saudi-supreme-court-announces-timing-for-arafat-day-and-eid-al-adha) | Saudi Supreme Court: Arafat Day Tue 26 May, Eid al-Adha Wed 27 May 2026 | Petra (Jordan News Agency) | 2026-05-17 |
| [S28](https://blog.zenhr.com/en/eid-al-adha-2026-holiday-saudi-arabia) | Eid al-Adha 2026 private sector: paid days off from Arafat Day (26 May), minimum 4 days | ZenHR | 2026-03-26 |
| [S29](https://blog.zenhr.com/en/saudi-arabia-public-holidays-2026-guide-to-ksa-holidays-dates) | 2026 Saudi holidays: Founding Day Sun 22 Feb, National Day Wed 23 Sep | ZenHR | 2025-12-21 |
| [S30](https://www.latestly.com/lifestyle/festivals-events/eid-2026-date-in-saudi-arabia-march-19-or-20-supreme-court-calls-for-moon-sighting-tomorrow-7357113.html/amp) | Ramadan 1447 began Wednesday 18 February 2026 in Saudi Arabia | LatestLY | 2026-03 |
| [S31](https://enterpriseam.com/ksa/2025/12/09/shoppers-tighten-grocery-spending-splurge-on-tech/) | NielsenIQ State of the Nation 3Q25: traditional trade 23% and e-commerce 5.6% of Saudi FMCG sales | Enterprise AM | 2025-12-09 |
| [S32](https://www.bordbia.ie/industry/news/food-alerts/retail-in-saudi-arabia/) | Baqalahs (small family-run grocers) ~57% of Saudi grocery market; hyper/supermarkets 37% (2018) | Bord Bia | 2018-04-13 |
| [S33](https://english.ajel.sa/saudi-arabia/mhrsd-announces-ramadan-working-hours-for-private-sector) | MHRSD: private-sector Ramadan working hours 6 per day (Art. 98) | Ajel | 2022-03-26 |
| [S34](https://beoe.gov.pk/foreign-jobs/3402034/3819070/10-storekeepers-required-in-saudi-arabia-salary-1500-2000-rial-sar) | Job posting: 10 storekeepers, KSA - SAR 1,500-2,000/month + free accommodation, food, transport, medical, air ticket | Bureau of Emigration & Overseas Employment, Govt of Pakistan | offer expiry 28/06/2025 |
| [S35](https://beoe.gov.pk/foreign-jobs/3141199/3536608/1-store-keeper-required-in-saudi-arabia-salary-2200-2500-rial-sar) | Job posting: store keeper, KSA - SAR 2,200-2,500/month | Bureau of Emigration & Overseas Employment, Govt of Pakistan | 2025 |
| [S36](https://saudiarabia.paylab.com/en/salaryinfo/management/warehouse-manager) | Warehouse manager, Saudi Arabia: gross SAR 3,240-11,953/month (10th-90th percentile), ~SAR 8,092 after 5 years (49 respondents) | Paylab Saudi Arabia | 2026 |
| [S37](https://saudiarabia.paylab.com/en/salaryinfo/management/storekeeper) | Storekeeper, Saudi Arabia: gross SAR 2,357-7,624/month (10th-90th percentile, all nationalities, 38 respondents) | Paylab Saudi Arabia | 2026 |
| [S38](https://multiples.vc/public-comps/almunajem-foods-valuation-multiples) | Almunajem Foods (listed Saudi food distributor): gross margin 16% (FY2025 and last twelve months) | multiples.vc | 2026-10-21 |
| S39 | Binzagr data walkthrough (Badr Almesri): costing per case / per pallet at facility level; client-specific rates (same case activity billed SAR 0.8 to one client, SAR 0.5 to another); labour posted per task (wage x task x duration); HU_ID_2 unused; zero-second tasks are an operator practice | Binzagr - meeting notes and transcript | 2026-10-21 |
| S40 | Binzagr WMS mappings: transaction mapping (cost / revenue, type of movement), WMS user list (payroll number, menu level, status), unit-of-sale conversion factors, pallet-id sample | Binzagr - Badr Almesri (files) | 2026-10-22 |

---

## 4. Parameters (low / base / high)

"Low" and "high" bracket the plausible range. For the labour-model parameters (M-codes), *low = conservative savings*, *high = stretch*. `Assumption` = no authoritative public figure; set from the nearest evidence and varied in the Monte Carlo. `Client` = stated by Binzagr; used only for checks.

| param_id | parameter | unit | low | base | high | basis | sources | note |
|---|---|---|---|---|---|---|---|---|
| L01 | basic_salary | SAR/month | 1400.0 | 1800.0 | 2300.0 | Researched | S02;S03;S01 | Postings: Riyadh warehouse worker 1,200-1,400; Jeddah labour 1,800-2,000. GASTAT non-Saudi male wages: no schooling 1,956, primary 2,088, lower secondary 2,305. |
| L02 | housing_pct_of_basic | share | 0.15 | 0.25 | 0.3 | Researched | S10;S02 | 25% of basic is the market norm (allowance); shared company accommodation is often cheaper -> low 15%. |
| L03 | transport | SAR/month | 100.0 | 200.0 | 300.0 | Assumption | S02 | Company transport is normally provided (posting S02); cost per head assumed. |
| L04 | food_allowance | SAR/month | 0.0 | 200.0 | 300.0 | Researched | S02 | SAR 200 food allowance in the Jeddah posting. |
| L05 | gosi_employer_rate_expat | share of basic+housing | 0.02 | 0.02 | 0.02 | Researched | S04;S05 | Non-Saudi: 2% occupational hazards, employer only. |
| L06 | expat_levy | SAR/month | 700.0 | 800.0 | 800.0 | Researched | S06 | ~SAR 800/worker/month; may be lower for establishments with a higher Saudi share. |
| L07 | iqama_fee | SAR/year | 650.0 | 650.0 | 650.0 | Researched | S06 | SAR 650 per 12 months. |
| L08 | medical_insurance | SAR/year | 800.0 | 1500.0 | 2500.0 | Assumption | S11 | Basic-class worker cover; national average premium per insured person is SAR 4,511 (all classes) - workers sit well below it. |
| L09 | eosb_months_per_year | months of wage | 0.5 | 0.5 | 0.5 | Researched | S09;S10 | Art. 84: half a month per year for the first 5 years; base = basic + housing. |
| L10 | air_ticket_biennial | SAR/2 years | 1500.0 | 2000.0 | 2500.0 | Assumption | S02 | Air ticket provided (S02); price assumed. |
| L11 | recruitment_visa_amortised | SAR per hire over 24 months | 4000.0 | 6000.0 | 10000.0 | Assumption |  | Visa, recruitment agency and onboarding, amortised over a 2-year contract. No authoritative public figure. |
| L20 | total_wage | SAR/month | 4000.0 | 5000.0 | 6000.0 | Researched | S12;S01 | Nitaqat counts a Saudi fully from SAR 4,000. GASTAT Saudi male lower-secondary average 7,352 (all occupations). |
| L21 | basic_share_of_wage | share | 0.7 | 0.75 | 0.8 | Assumption | S10 | Basic ~75-80% of wage when housing is 25% of basic. |
| L22 | gosi_employer_rate_saudi | share of basic+housing | 0.1175 | 0.1225 | 0.1275 | Researched | S04;S05 | Old scheme 11.75%; new scheme 12.25% (to Jun 2026), 12.75% (from Jul 2026). |
| L23 | saudi_share_of_operatives | share | 0.1 | 0.2 | 0.3 | Assumption |  | Actual workforce mix is unknown (see 04_Gaps). |
| L12 | storekeeper_basic_salary | SAR/month | 1500.0 | 1900.0 | 2500.0 | Researched | S34;S35;S37 | Expat storekeeper postings SAR 1,500-2,500 basic + benefits (same band as operatives); Paylab gross 2,357-7,624 (all nationalities). Tally clerks use the operative basic salary (L01). |
| L24 | manager_monthly_wage | SAR/month | 5000.0 | 8000.0 | 12000.0 | Researched | S36 | Paylab warehouse manager gross SAR 3,240-11,953 (P10-P90), ~8,092 after 5 years. |
| L25 | manager_oncost_share | share of wage | 0.15 | 0.2 | 0.3 | Assumption | S04;S06;S09 | GOSI, medical, end of service and (if expat) levy / iqama on top of the cash wage. |
| T01 | working_days_per_month | days | 26.0 | 26.0 | 26.0 | Researched | S07 | 6-day week, Friday rest (Art. 98/104). |
| T02 | daily_hours | hours | 8.0 | 8.0 | 8.0 | Researched | S07 | Art. 98. |
| T03 | daily_hours_ramadan | hours | 6.0 | 6.0 | 6.0 | Researched | S07;S33 | Art. 98 / MHRSD. |
| T04 | break_hours | hours | 0.5 | 0.5 | 0.5 | Researched | S07 | Art. 101: 30-minute rest after 5 hours. |
| T05 | max_hours_at_workplace | hours | 12.0 | 12.0 | 12.0 | Researched | S07 | Art. 101. |
| T06 | overtime_premium_on_basic | share | 0.5 | 0.5 | 0.5 | Researched | S08 | Art. 107. |
| T07 | annual_leave_days | days/year | 21.0 | 21.0 | 21.0 | Researched | S09 | Art. 109 (30 after 5 years). |
| T08 | public_holiday_days | days/year | 10.0 | 10.0 | 12.0 | Researched | S25;S28;S29 | Eid al-Fitr 4 + Eid al-Adha 4 + Founding Day + National Day. |
| T09 | pfd_allowance | share | 0.1 | 0.12 | 0.14 | Researched | S13 | Personal ~5% + fatigue 3-5% + delay 2-4%. |
| C01 | labour_share_of_warehouse_opex | share | 0.4 | 0.5 | 0.6 | Researched | S15;S14 | Labour up to ~50% of opex; order picking alone up to 55%. |
| C02 | ebit_margin_3pl | share | 0.04 | 0.065 | 0.1 | Researched | S16 | DHL Supply Chain FY2025 EBIT margin 6.5%. |
| C03 | us_receiving_fee_per_pallet | USD | 5.0 | 10.52 | 15.0 | Researched | S17;S18 | Benchmark only (cross-check of the rate card). |
| C04 | us_receiving_labour_rate | USD/hour | 30.0 | 39.88 | 60.0 | Researched | S17 | Benchmark only. |
| C05 | ksa_storage_per_pallet_month | SAR | 80.0 | 150.0 | 250.0 | Researched | S19 | Storage revenue is NOT modelled (pallet positions unknown) - context only. |
| C06 | client_case_rate_example | SAR/case | 0.5 | 0.65 | 0.8 | Client | S39 | Badr: the same case activity is billed SAR 0.8 to one client and SAR 0.5 to another. Used only to check the simulated case-pick rate. |
| C07 | distributor_gross_margin | share | 0.12 | 0.16 | 0.2 | Researched | S38 | Turns extra Riyadh sales into profit in the opportunity levers (no Binzagr cost data yet). |
| M01 | cycle_idle_cap_minutes | minutes | 15.0 | 30.0 | 45.0 | Assumption |  | Gap between scans longer than this is a break/idle, not work content. |
| M02 | standard_time_percentile | percentile of badge-month mean cycle | 0.6 | 0.5 | 0.4 | Assumption |  | Standard = time the median performer needs (low = more lenient, high = tighter). |
| M03 | target_performance_percentile | percentile of shift performance | 0.6 | 0.75 | 0.9 | Assumption | S23 | Target = what the best X% of Binzagr shifts already achieve. Base P75 (top quartile). |
| M04 | direct_shift_min_events | events | 30.0 | 30.0 | 30.0 | Assumption |  | Shifts with fewer scans are treated as indirect/occasional (excluded from savings). |
| M05 | savings_capture_rate | share | 0.5 | 0.7 | 0.9 | Assumption | S23 | Share of the modelled gap that is realistically captured (rostering is lumpy). |
| M06 | badge_sharing_split_hours | hours | 12.0 | 12.0 | 12.0 | Researched | S07 | Sessions longer than the 12h legal presence limit are split into two worker-shifts. |
| M07 | bulk_posting_same_second_share | share of scans | 0.85 | 0.9 | 0.95 | Assumption |  | Sessions with more than this share of scans in the same second as the previous scan are batch confirmations (95th percentile of sessions = 0.89); paid hours count, but they are not benchmarked. |

---

## 5. Methods and results

### 5.1 Loaded labour cost by role (Simulated)
Binzagr's user list gives each badge a WMS role: **tally clerk** (91,893 paid hours), **storekeeper** (50,226 h), **warehouse manager** (112 h), and logins not in the list (44,149 h). Labour is costed per role, as Binzagr posts it (wage × task × duration, [S39]):
- **Operative** (tally clerk, and logins not in the list): cash pay (basic, housing, transport, food) + GOSI ([S04]), expat levy and iqama ([S06]), medical insurance, end-of-service accrual (Art. 84, [S09]), air ticket, recruitment / visa; blended with an assumed Saudi share ([S12]).
- **Storekeeper:** the same build-up on a storekeeper basic salary (expat postings SAR 1,500–2,500, [S34], [S35]) — research shows no real premium over operatives.
- **Supervisory** (warehouse manager): cash wage SAR 5,000–12,000 ([S36]) plus on-costs; never benchmarked for savings.
Cost per worked shift spreads paid leave (21 days, [S09]) and public holidays over 281 worked days a year. Overtime = hourly wage + 50% of basic (Art. 107, [S08]).

Expat operative and Saudi operative build-up (SAR / month):

| role | component | low | base | high |
|---|---|---|---|---|
| operative (expat) | basic | 1,400 | 1,800 | 2,300 |
| operative (expat) | housing | 210 | 450 | 690 |
| operative (expat) | transport | 100 | 200 | 300 |
| operative (expat) | food | 0 | 200 | 300 |
| operative (expat) | gosi_employer | 32 | 45 | 60 |
| operative (expat) | expat_levy | 700 | 800 | 800 |
| operative (expat) | iqama | 54 | 54 | 54 |
| operative (expat) | medical | 67 | 125 | 208 |
| operative (expat) | eosb_accrual | 67 | 94 | 125 |
| operative (expat) | air_ticket | 62 | 83 | 104 |
| operative (expat) | recruitment_visa | 167 | 250 | 417 |
| operative (expat) | TOTAL | 2,859 | 4,101 | 5,358 |
| saudi operative | gosi_employer | 470 | 612 | 765 |
| saudi operative | medical | 67 | 125 | 208 |
| saudi operative | eosb_accrual | 167 | 208 | 250 |
| saudi operative | TOTAL | 4,703 | 5,946 | 7,223 |
| saudi operative | wage | 4,000 | 5,000 | 6,000 |

Per role tier:

| role | component | low | base | high |
|---|---|---|---|---|
| operative (blended) | blended_month | 3,043.69 | 4,470.17 | 5,917.40 |
| operative (blended) | cost_per_worked_shift | 129.98 | 190.90 | 254.51 |
| operative (blended) | cost_per_paid_hour | 16.25 | 23.86 | 31.81 |
| operative (blended) | overtime_cost_per_hour | 11.29 | 17.56 | 24.32 |
| storekeeper (blended) | blended_month | 3,153.57 | 4,576.33 | 6,110.62 |
| storekeeper (blended) | cost_per_worked_shift | 134.67 | 195.43 | 262.82 |
| storekeeper (blended) | cost_per_paid_hour | 16.83 | 24.43 | 32.85 |
| storekeeper (blended) | overtime_cost_per_hour | 11.91 | 18.15 | 25.38 |
| supervisory (blended) | blended_month | 5,750.00 | 9,600.00 | 15,600.00 |
| supervisory (blended) | cost_per_worked_shift | 245.55 | 409.96 | 670.97 |
| supervisory (blended) | cost_per_paid_hour | 30.69 | 51.25 | 83.87 |
| supervisory (blended) | overtime_cost_per_hour | 30.69 | 51.25 | 83.87 |

### 5.2 Work content per scan (Inferred)
For each badge's timeline: **cycle time = idle time since the previous scan + own duration**; idle gaps longer than 30 minutes count as breaks and tasks left open are capped at 1 h. Records written in the same second (e.g. *Case Pick* next to *Picking (Pick)*, billing echo rows) get zero, so time is not double-counted — Binzagr confirmed that zero-second tasks are an operator practice ([S39]), so the recorded duration alone is not used. Total scan-evidenced work: 51,084 h over 8 months. Variants with 15 / 45-minute caps go into the Monte Carlo.

### 5.3 Standard times by activity and line size (Inferred)
New in version 2: Binzagr's conversion factors give every scan its quantity in **cases**, so the standard is set per activity **and line-size band** (cases per line). It matters for physical moves: a *Picking (Pick)* line of one case takes about a minute, a line of 11–25 cases more than two. With a single per-line standard, shifts that pick big lines would look slow. For each activity × band: the mean cycle time of every badge-month with ≥ 30 such scans; **standard = median of those means** (P50; lenient P60 and tight P40 variants). Sparse bands use the pooled band mean (scaled to the variant), and lines without a factor use the activity-level standard. Standards are Jeddah-wide so sites can be compared. Median standard (seconds per line), idle cap 30 min:

| tran_desc | <=1 | 2-4 | 5-10 | 11-25 | 26-60 | >60 |
|---|---|---|---|---|---|---|
| Picking (Pick) | 146 | 168 | 220 | 291 | 231 | 186 |
| Loading (Pick) | 8 | 8 | 9 | 15 | 19 | 20 |
| Consolidation (Pick/Put) | 14 | 12 | 18 | 28 | 39 | 35 |
| Receipt of Shipment (Rcpt) | 121 | 136 | 167 | 186 | 81 | 91 |
| Move (Put) | 23 | 20 | 22 | 26 | 40 | 36 |
| Directed Pickup (Pick) | 124 | 140 | 157 | 164 | 124 | 136 |

### 5.4 Simulated attendance (paid time) per person under Saudi Labour Law
No roster exists, so each **person work session** is one paid shift. New in version 2: badges of the same payroll number are merged (a session ends after > 6 h without scans across all of the person's badges), so a person using two badges is paid once. Unmapped logins stay one person per badge.
- Legal hours 8/day, 6/day in Ramadan (18 Feb – 19 Mar) (Art. 98, [S07], [S33]); 30-min break (Art. 101).
- Overtime = span beyond legal hours + break, up to the 12 h presence limit (Art. 101), paid per Art. 107.
- **Sessions longer than 12 h** are legally impossible for one person → counted as **two** shifts (368 sessions; [S24] for context).
- 445 person sessions used two or more badges; in some, two badges of one person were active **at the same time** (258 h) — flagged for the badge-sharing review.
- System logins (SAPImport, Admin, ShortPick) excluded.

Result: 23,589 person sessions → 186,380 paid hours on the days people scanned (incl. 1,390 overtime) ≈ **115 FTE** — a *lower bound*: a salaried person is also paid on the days they do not scan (§5.4b).

| wms_role | sessions | paid_h | labour_cost | recoverable_h |
|---|---|---|---|---|
| Tally clerk | 11,532 | 91,893 | 2,271,656 | 10,695 |
| Storekeeper | 6,357 | 50,226 | 1,265,427 | 5,854 |
| Not in user list | 5,686 | 44,149 | 1,087,410 | 839 |
| Warehouse manager | 14 | 112 | 5,740 | 0 |

### 5.4b Head count from employee ids (new in version 3)
Mehul's point: the employee ids give a head count, which beats a head count built from shifts. Three sources, from the hardest to the softest:
- **Binzagr's user list** (`02_Cleaned/ref_wms_user_headcount_month`, Actual): 269 people a month on average hold an active login at the 9 sites in the log (285 across all Jeddah sites). Only about half of them scan in a given month, so the list alone overstates who works here.
- **Employee ids in the scans** (Actual): 202 ids scan in an average month — 138 payroll-confirmed people and 64 logins the list does not know (the 38 desktop `WA.AD.nnn` logins are merged with their plain-number twin; system logins excluded). Over Feb–Sep: 193 people and 104 other logins.
- **Employment spells** (Inferred): each id counts from its first to its last scan day (from 1 Feb / to 30 Sep when seen in the first / last week of the extract). A salaried person is paid on every working day (Sat–Thu) of the spell; 90% of those days are expected to be worked after annual leave and public holidays. Payroll-confirmed people scanned on 73% of the days they were expected to work.

Three paid-time bases (all with base cost parameters):

| Basis | Who is paid when | FTE | Labour cost, Feb–Sep | Paid time with no scan |
|---|---|---|---|---|
| Scan days (v2, low) | everyone only on days they scan | 115 | SAR 4,630,232 | 46.0% |
| **Payroll head count (v3 base)** | payroll-confirmed people on every working day of their spell; other logins on days they scan | **161** | **SAR 6,146,784** | **58.9%** |
| Every employee id salaried (high) | every login, incl. the 64 a month the list does not know | 199 | SAR 7,539,808 | 66.6% |

By month:

| month | registered_active_people_log_sites | employee_ids_scanning | payroll_people_scanning | other_logins_scanning | fte_scan_days | fte_headcount_base | fte_all_ids | paid_days_without_scan_base | labour_cost_scan_days_sar | labour_cost_headcount_base_sar | labour_cost_all_ids_sar | unobserved_share_headcount_base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-02 | 274 | 226 | 164 | 62 | 127 | 176 | 204 | 767 | 640,126 | 792,759 | 904,893 | 54.7% |
| 2026-03 | 272 | 228 | 160 | 68 | 120 | 184 | 225 | 1,418 | 607,117 | 885,886 | 1,081,904 | 64.6% |
| 2026-04 | 275 | 229 | 157 | 72 | 137 | 187 | 229 | 1,062 | 687,651 | 897,788 | 1,099,119 | 61.2% |
| 2026-05 | 276 | 192 | 133 | 59 | 98 | 159 | 206 | 1,273 | 494,999 | 745,631 | 953,814 | 64.1% |
| 2026-06 | 261 | 187 | 122 | 65 | 108 | 158 | 202 | 1,080 | 539,960 | 753,757 | 960,890 | 61.0% |
| 2026-07 | 263 | 189 | 124 | 65 | 115 | 153 | 194 | 800 | 577,003 | 736,590 | 928,776 | 56.0% |
| 2026-08 | 263 | 198 | 129 | 69 | 114 | 146 | 181 | 775 | 572,246 | 726,618 | 902,907 | 56.1% |
| 2026-09 | 265 | 169 | 113 | 56 | 102 | 128 | 148 | 477 | 511,132 | 607,755 | 707,505 | 51.6% |

The head count falls from February to September (fewer ids scan each month), and the gap between the user list and the people who scan is large: those accounts may belong to people on leave, people working at other sites, staff whose work is not scanned, or stale logins. **Attendance by payroll number (G2) and one month of actual labour cost (G3) decide which basis is right.** Recoverable hours do not depend on the basis: they come from the shifts people actually worked.

### 5.5 Benchmarking shifts and recoverable hours (Inferred → Simulated)
**Why not compare against absolute standards?** Scans cover only part of the work; comparing earned standard hours with full paid shifts would claim ~75% savings — the kind of number that would be "dismissed out of hand".

**Method (like-for-like, own-data benchmark):**
1. Each session gets an **activity role** = its dominant activity family (Picking, Loading / shipping, Internal movement, …).
2. **Observed time** = the scanned span (capped by paid hours); **unobserved** = paid time outside it (reported, never claimed).
3. **Span productivity** = earned standard hours ÷ observed hours, for *full* shifts (≥ 30 scans, ≥ 4 h span): 12,043 shifts.
4. Not benchmarked: supervisory staff, and **batch-posting sessions** — more than 90% of scans in the same second as the previous scan (871 sessions, 6,748 paid hours; mostly the `WA.AD.nnn`, HM1008 and AMR2100 logins). Their paid hours still count. Badge BHAS210004 (a storekeeper login, [S40]) batch-posts too but most of its sessions stay under the threshold; excluding it entirely would lower recoverable hours by about 1%.
5. **Target** = the top-quartile (P75) productivity of shifts **in the same role** (roles with < 30 full shifts get no target).
6. **Recoverable hours** = observed hours − earned ÷ target (if positive) × **capture rate** 70%; valued at the person's role cost.

| role | eligible_shifts | median_productivity | target_p75 | observed_h | recoverable_h | recoverable_pct_of_observed |
|---|---|---|---|---|---|---|
| Picking | 6762 | 0.51 | 0.69 | 47,414 | 9,216 | 19.4% |
| Loading / shipping | 1725 | 0.25 | 0.44 | 11,112 | 3,029 | 27.3% |
| Internal movement | 1627 | 0.53 | 0.74 | 11,746 | 2,508 | 21.4% |
| Inbound | 764 | 0.48 | 0.63 | 5,041 | 958 | 19.0% |
| Inbound admin | 450 | 0.07 | 0.10 | 2,751 | 716 | 26.0% |
| Inventory control | 368 | 0.21 | 0.29 | 2,469 | 472 | 19.1% |
| Consolidation | 141 | 0.14 | 0.22 | 882 | 216 | 24.5% |
| Returns | 88 | 0.39 | 0.50 | 548 | 103 | 18.7% |
| Replenishment | 86 | 0.39 | 0.58 | 600 | 126 | 21.1% |
| Quality control | 32 | 0.05 | 0.08 | 196 | 43 | 21.8% |
| Outbound admin | 8 | 0.22 |  | 48 | 0 | 0.0% |
| Inter-warehouse transfer | 2 | 0.10 |  | 19 | 0 | 0.0% |

By warehouse (base, Feb–Sep):

| wh_id | sessions | paid_h | unobserved_pct | recoverable_h | recoverable_pct | labour_cost | recoverable_sar | badge_sharing_splits | temperature_regime_inferred |
|---|---|---|---|---|---|---|---|---|---|
| 2100-04 | 12,015 | 96,313 | 33.6% | 10,803 | 11.2% | 2,389,484 | 265,333 | 267 | likely ambient |
| 2100-01 | 5,033 | 39,192 | 55.2% | 3,271 | 8.3% | 973,195 | 79,464 | 49 | likely ambient |
| 2100-07 | 2,880 | 22,848 | 54.8% | 2,193 | 9.6% | 559,961 | 53,218 | 29 | likely ambient |
| 2100-15 | 1,318 | 10,304 | 68.9% | 578 | 5.6% | 253,192 | 14,154 | 2 | likely cold-chain |
| 2100-12 | 1,332 | 9,938 | 57.3% | 492 | 4.9% | 260,749 | 12,874 | 21 | likely ambient |
| 2100 | 849 | 6,563 | 84.3% | 0 | 0.0% | 162,281 | 0 | 0 | likely ambient |
| 2100-14 | 162 | 1,222 | 67.1% | 51 | 4.2% | 31,371 | 1,280 | 0 | likely cold-chain |

### 5.6 Task-based cost posting, cost per case and per pallet (Simulated) — new
Binzagr posts labour cost per task (wage × task × duration, per user) and costs the facility per case and per pallet ([S39]). Here each person session's paid cost is spread over the tasks in it by cycle time (time the scans cannot see is spread pro rata), so the full labour cost lands on activities — reconciled to the riyal (check S15). Binzagr's classification of each activity ([S40]) then shows where labour goes:

| movement_type | events | labour_cost | share_of_labour_cost |
|---|---|---|---|
| Duplicate | 2,124,410 | 2,565,075 | 55.4% |
| Basic Service | 1,376,297 | 939,209 | 20.3% |
| Added Value | 822,299 | 630,253 | 13.6% |
| In House | 159,325 | 478,411 | 10.3% |
| Duplicate / In House | 4,196 | 17,284 | 0.4% |

*Duplicate* in Binzagr's mapping means "billed under another code" (e.g. the *Picking (Pick)* scan, whose billing sits on *Case Pick* / *Pallet Pick*), not wasted work. *In House* is work no client pays for. Largest activities:

| tran_desc | movement_type | cost_or_revenue | events | labour_cost | share | cost_per_event |
|---|---|---|---|---|---|---|
| Picking (Pick) | Duplicate | Cost | 516,761 | 1,461,401 | 31.6% | 2.83 |
| RoS Shipment Audit | Duplicate | Cost | 37,694 | 361,560 | 7.8% | 9.59 |
| Move (Pick) | In House | Cost | 54,492 | 260,369 | 5.6% | 4.78 |
| Loading (Pick) | Duplicate | Cost | 466,133 | 257,961 | 5.6% | 0.55 |
| Receipt of Shipment (Rcpt) | Basic Service | Revenue Generation | 117,453 | 249,189 | 5.4% | 2.12 |
| Consolidation (Pick/Put) | Added Value | Revenue Generation | 278,651 | 231,466 | 5.0% | 0.83 |
| Shipment Reconciliation | Basic Service | Cost | 123,622 | 210,019 | 4.5% | 1.70 |
| Outbound Order Closure | Basic Service | Cost | 3,616 | 169,779 | 3.7% | 46.95 |
| LP Audit | Added Value | Cost | 85,084 | 159,581 | 3.4% | 1.88 |
| Directed Pickup (Pick) | Duplicate | Cost | 92,807 | 159,125 | 3.4% | 1.71 |
| Picking (Put) | Duplicate | Cost | 493,760 | 157,108 | 3.4% | 0.32 |
| Close PO | Basic Service | Cost | 7,926 | 99,194 | 2.1% | 12.52 |
| Move (Put) | In House | Cost | 55,305 | 82,997 | 1.8% | 1.50 |
| QC Hold License Plate | Added Value | Cost | 8,181 | 82,824 | 1.8% | 10.12 |
| Replenishment (Pick) | In House | Cost | 14,787 | 80,905 | 1.7% | 5.47 |

Labour cost per unit, Feb–Sep by site, on the head-count basis (each site-month's overhead and paid days without a scan spread over its families; `per_loose_case_worked_time` = days with scans only). Outbound family (picking, loading, LP audit, QC) incl. its share of overhead is split between case picks and pallet picks by billed lines; that split matches actual pick time (80.4% vs 80.1% for case picks: 100.0% of 516,761 *Picking (Pick)* scans were linked to the *Case Pick* / *Pallet Pick* row stamped when they end; check S19). Inbound family is split the same way between receipts, damaged-goods receipts and unloads.

| wh_id | labour_cost_sar | loose_cases_picked | per_loose_case_worked_time | per_loose_case | cases_shipped | per_case_shipped | pallets_picked | per_pallet_picked | pallets_received | per_pallet_received |
|---|---|---|---|---|---|---|---|---|---|---|
| 2100-04 | 3,195,379 | 1,696,915 | 0.74 | 0.99 | 4,047,554 | 0.51 | 63,921 | 6.22 | 74,735 | 11.58 |
| 2100-01 | 1,282,718 | 663,809 | 0.61 | 0.81 | 1,844,073 | 0.38 | 14,706 | 10.45 | 17,481 | 26.63 |
| 2100-07 | 718,033 | 307,330 | 0.85 | 1.09 | 631,209 | 0.62 | 6,039 | 9.93 | 13,172 | 20.17 |
| 2100-12 | 383,416 | 213,995 | 0.54 | 0.73 | 469,956 | 0.40 | 5,362 | 5.95 | 4,688 | 27.90 |
| 2100-15 | 327,884 | 72,214 | 0.73 | 0.94 | 282,903 | 0.41 | 4,400 | 10.72 | 5,992 | 30.03 |
| 2100 | 203,499 | 0 |  |  | 0 |  | 0 |  | 0 |  |
| 2100-14 | 35,854 | 3,145 | 0.28 | 0.38 | 145,409 | 0.16 | 2,162 | 10.35 | 1,385 | 8.41 |
| 2100-02 | 0 | 0 |  |  | 0 |  | 0 |  | 0 |  |

Jeddah overall, days people worked → all paid days: **SAR 0.77 → 1.02 per loose case picked**, SAR 5.70 → 7.57 per pallet picked and **SAR 12.10 → 16.06 per pallet received**; per case shipped (loose + full pallets) SAR 0.38 → 0.50, or SAR 0.83 with all labour. The Jeddah figures use the rate-card allocation (overhead spread over all families together), so they equal the rate card's labour per unit; the site table spreads each site-month's overhead over its own families. Binzagr's facility cost per case / per pallet is the natural validation (04_Gaps V11).

### 5.7 Rate card (Simulated, cost-recovery) on Binzagr's revenue codes
New in version 2: only the activities Binzagr marks **Revenue Generation** are billed — inbound receipt, damaged-goods receipt, unload / unpick, **case pick (per case)**, pallet pick, consolidation, re-palletisation, pallet split, returns put-away. Loading, LP audit and QC are *cost* in Binzagr's mapping, so they are no longer billed; the three "Per Pallet (Billing)" rows are *Duplicate*. Code 100 is excluded (mapping says "Picked Volume", the log says "Item Import" — to confirm).

Allocation: posted labour is grouped into five service families (the billed activity + the cost activities that support it); warehouse overhead (in-house moves, counts, QC, adjustments: 14.0% of labour) and the paid days without a scan (24.7% of labour on the head-count basis) are spread pro rata; each family is split over its billed activities by billed lines; then grossed up for non-labour opex (labour ≈ 50% of warehouse opex, [S15], [S14]) and a 6.5% EBIT margin ([S16]).

| service | unit | units_8_months | share_of_labour_cost | worked_time_labour_per_unit_sar | labour_cost_per_unit_sar | rate_low_sar | rate_base_sar | rate_high_sar | external_check_sar |
|---|---|---|---|---|---|---|---|---|---|
| Case pick | case picked | 2,957,408 | 48.9% | 0.77 | 1.02 | 1.81 | 2.17 | 2.53 | 0.65 |
| Consolidation | line consolidated | 278,651 | 6.1% | 1.02 | 1.36 | 2.41 | 2.90 | 3.37 |  |
| Damaged-goods receipt | damaged pallet received | 940 | 0.2% | 12.10 | 16.06 | 28.54 | 34.35 | 39.88 |  |
| Inbound receipt | pallet received | 117,453 | 30.7% | 12.10 | 16.06 | 28.54 | 34.35 | 39.88 | 13.46 |
| Pallet pick | pallet picked | 96,590 | 11.9% | 5.70 | 7.57 | 13.45 | 16.19 | 18.80 |  |
| Pallet split | pallet split | 1,067 | 0.0% | 1.45 | 1.92 | 3.42 | 4.11 | 4.77 |  |
| Re-palletisation | pallet re-palletised | 3,176 | 0.1% | 1.45 | 1.92 | 3.42 | 4.11 | 4.77 |  |
| Returns put-away | return put away | 9,802 | 1.3% | 6.08 | 8.07 | 14.33 | 17.25 | 20.03 |  |
| Unload / unpick | pallet unloaded / unpicked | 2,636 | 0.7% | 12.10 | 16.06 | 28.54 | 34.35 | 39.88 |  |

**Checks against the outside world** (S08b–d):
- **Case pick vs Binzagr's own prices:** labour per loose case on the days people worked (SAR 0.77) is not above the highest of the SAR 0.50–0.80 per case Badr quoted (PASS: otherwise every client would be priced below labour), but it is above the lowest — a question for Binzagr, not a model error (V12). With the paid days without a scan it is SAR 1.02: Binzagr's facility cost per case (V11) shows which is closer. The *full cost-recovery* case rate (SAR 2.17) is 2–3× his figures: because storage revenue is not modelled, this rate card has to carry all non-labour cost and margin on handling. Binzagr's real price list therefore matters more than any external benchmark (information warning).
- **Receiving vs a US survey** (USD 10.52 per pallet ÷ USD 39.88/h = 0.264 billing-hours per pallet at SAR 51.0/h = SAR 13.46) vs SAR 34.35 here: ours carries the inbound paperwork (14.5% of labour) and the opex gross-up (information warning; in version 1 this was a pass/fail test at ±50% — with Binzagr's real price signal available, a US handling fee is no longer a fair pass/fail benchmark).
- Storage revenue is **not** modelled (pallet positions unknown; market SAR 80–250 / pallet / month, [S19]).

### 5.8 Simulated monthly P&L (base)
Revenue = Actual billed units × base rates. Labour = head-count basis (paid shifts on scan days × role cost + paid days without a scan). Other opex = labour × (1/0.5 − 1). Optimised = labour − recoverable cost. **Baseline margin = 6.5% by construction.** `fte_equivalent` = people paid on the head-count basis; `fte_scan_days` = the version-2 figure.

| month | handling_revenue_sar | labour_cost_sar | labour_cost_paid_days_without_scan_sar | other_opex_sar | ebit_sar | recoverable_labour_sar | ebit_margin | ebit_margin_optimised | employee_ids_scanning | fte_equivalent | fte_scan_days | fte_recoverable |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-02 | 1,924,525 | 792,759 | 152,633 | 792,759 | 339,007 | 53,766 | 17.6% | 20.4% | 226 | 176 | 127 | 10.8 |
| 2026-03 | 1,770,563 | 885,886 | 278,769 | 885,886 | -1,208 | 35,451 | -0.1% | 1.9% | 228 | 184 | 120 | 7.1 |
| 2026-04 | 1,887,531 | 897,788 | 210,137 | 897,788 | 91,956 | 62,789 | 4.9% | 8.2% | 229 | 187 | 137 | 12.6 |
| 2026-05 | 1,530,050 | 745,631 | 250,632 | 745,631 | 38,789 | 45,681 | 2.5% | 5.5% | 192 | 159 | 98 | 9.1 |
| 2026-06 | 1,537,105 | 753,757 | 213,798 | 753,757 | 29,590 | 54,359 | 1.9% | 5.5% | 187 | 158 | 108 | 10.9 |
| 2026-07 | 1,600,721 | 736,590 | 159,587 | 736,590 | 127,541 | 58,357 | 8.0% | 11.6% | 189 | 153 | 115 | 11.7 |
| 2026-08 | 1,500,573 | 726,618 | 154,371 | 726,618 | 47,337 | 59,774 | 3.2% | 7.1% | 198 | 146 | 114 | 11.9 |
| 2026-09 | 1,397,131 | 607,755 | 96,623 | 607,755 | 181,621 | 56,146 | 13.0% | 17.0% | 169 | 128 | 102 | 11.2 |

### 5.9 Scenarios and Monte Carlo
Deterministic scenarios:

| scenario | cost_parameters | paid_time_basis | fte_paid | standard | target_percentile | capture_rate | labour_cost_annual_sar | recoverable_labour_annual_sar | recoverable_pct_of_labour | fte_recoverable | ebit_margin | ebit_margin_optimised |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Conservative | low | scan_days | 115 | lenient_p60 | 0.6 | 0.5 | 4,742,559 | 218,740 | 4.6% | 5.3 | 4.0% | 5.8% |
| Base | base | headcount_base | 161 | median_p50 | 0.75 | 0.7 | 9,220,175 | 639,485 | 6.9% | 10.7 | 6.5% | 9.7% |
| Stretch | high | all_ids | 199 | tight_p40 | 0.9 | 0.9 | 15,150,673 | 1,578,771 | 10.4% | 19.7 | 10.0% | 15.6% |

Monte Carlo: 5,000 draws; every labour-cost (all role tiers), cost-structure and model parameter drawn from a triangular(low, base, high) distribution; standard variant (P60/P50/P40), idle cap (15/30/45 min) and paid-time basis (scan days / payroll head count / every id) drawn 25/50/25. The basis moves the labour cost (median SAR 6,922,202 / 9,216,077 / 11,280,633 a year), not the recoverable labour.

| metric | p10 | p50 | p90 |
|---|---|---|---|
| labour_cost_annual_sar | 6,838,024 | 9,214,394 | 11,415,787 |
| recoverable_labour_annual_sar | 491,647 | 633,749 | 805,806 |
| recoverable_pct_of_labour | 5.1% | 7.0% | 9.9% |
| recoverable_hours_annual | 20,267 | 25,885 | 32,591 |
| fte_recoverable | 8.3 | 10.6 | 13.3 |
| ebit_margin_optimised | 8.3% | 10.1% | 12.1% |

**What drives the answer** (Spearman rank correlation with recoverable SAR):

| input | spearman_rank_corr_with_recoverable_SAR |
|---|---|
| target_percentile | 0.74 |
| capture_rate | 0.59 |
| basic_salary | 0.13 |
| total_wage | 0.10 |
| saudi_share_of_operatives | 0.08 |
| housing_pct_of_basic | 0.07 |
| food_allowance | 0.07 |
| storekeeper_basic_salary | 0.06 |
| idle_cap_min | -0.06 |
| recruitment_visa_amortised | 0.05 |

The result is driven by **management choices** (how ambitious the target is, how much of the gap is captured), far more than by wage assumptions.

### 5.10 Badge-sharing risk per person session (Inferred)
Rules (span > 12 h / > 16 h, warehouse switch < 2 min, extreme scan rate, **two badges of one person active together > 30 min**) + an **IsolationForest** anomaly model on session behaviour (span, scans, scans/hour, sites, fast switches, badges used, concurrent hours, same-second share; 300 trees, 3% contamination). Tiers: none 22,476, low 488, medium 427, high 198. Sessions flagged for concurrent badges: 97. Badges with most high/medium sessions: MDAA210004 (99), HM1008 (57), AHH2100004 (34), MDM210001|MDM210007 (31), MHA210004 (25), BB210004 (22). These are **candidates to check against attendance**, not findings.

### 5.11 Clients and warehouses (Inferred)
Client code → principal via item code → product master (26 Binzagr business units, 5 external 3PL clients):

| client_code | sku_events | client_type_inferred | principals_top3 | confidence |
|---|---|---|---|---|
| 5001 | 1,506,611 | binzagr_business_unit | Binzagr Unilever Distribution Ltd (90%); Lipton Teas and Infusions (10%) | high |
| 8007 | 773,274 | binzagr_business_unit | Dabur International Limited (76%); Saudi Industrial Detergent Co. (22%); WaterWipes UC (2%) | medium |
| 5003 | 429,022 | binzagr_business_unit | Carlsberg (88%); International Beverage & Filling Ind LLC (11%); BINZAGR CO-RO LTD (0%) | high |
| 3069 | 364,828 | external_3pl_client |  | high (own item codes, 0% match) |
| 5004 | 279,729 | binzagr_business_unit | UNIPEX DAIRY PROD.CO.LTD (51%); HERSHEY INTERNATIONAL (20%); SEVILLE PRODUCTS LTD (17%) | medium |
| 5002 | 276,727 | binzagr_business_unit | BINZAGR FOODS (43%); Fonterra (27%); HEINZ AFRICA & MIDDLE EAST (13%) | low (multi-principal unit) |
| 8011 | 210,527 | binzagr_business_unit | Johnson's (100%) | high |
| 6005 | 124,605 | binzagr_business_unit | MCCAIN INTERNATIONAL INC (57%); HEINZ AFRICA & MIDDLE EAST (25%); UNIPEX DAIRY PROD.CO.LTD (6%) | medium |
| 3030 | 34,975 | external_3pl_client |  | high (own item codes, 0% match) |
| 5007 | 34,423 | binzagr_business_unit | SANPELLEGRINO.S.P.A. (41%); Kikkoman Corporation (24%); WaterWipes UC (15%) | low (multi-principal unit) |
| 5005 | 28,245 | binzagr_business_unit | MCCAIN INTERNATIONAL INC (57%); Lipton Teas and Infusions (20%); BINZAGR FOODS (12%) | medium |
| 3024 | 15,702 | external_3pl_client |  | high (own item codes, 0% match) |

Warehouse timelines (structural changes = month-on-month change ≥ 50%):

| wh_id | rows_total | first_month | last_month | structural_changes | external_3pl_share | temperature_regime_inferred |
|---|---|---|---|---|---|---|
| 2100-04 | 2,998,844 | 2026-02 | 2026-09 |  | 14.8% | likely ambient |
| 2100-01 | 661,045 | 2026-02 | 2026-09 | 2026-08: -61%; 2026-09: -100% | 0.5% | likely ambient |
| 2100-07 | 387,437 | 2026-02 | 2026-09 | 2026-08: +279% | 1.6% | likely ambient |
| 2100-12 | 335,618 | 2026-02 | 2026-08 | 2026-05: -100% | 0.0% | likely ambient |
| 2100-15 | 113,476 | 2026-02 | 2026-09 |  | 8.3% | likely cold-chain |
| 2100-14 | 23,347 | 2026-02 | 2026-09 | 2026-05: -91%; 2026-06: +925%; 2026-07: -78%; 2026-08: +270%; 2026-09: -63% | 0.0% | likely cold-chain |
| 2100 | 19,490 | 2026-02 | 2026-09 |  | 0.6% | likely ambient |
| 2100-16 | 275 | 2026-02 | 2026-09 |  | 0.0% | likely ambient |
| 2100-02 | 1 | 2026-07 | 2026-07 |  | 0.0% | unknown |

The data confirms the **2100-01 → 2100-07 move in August** and **2100-12 going silent after April**. 2100-14/15 handle mostly frozen/chilled lines (McCain, dairy) — benchmark them separately once confirmed. Bonded-warehouse services sit at King Abdullah Port, not in Jeddah ([S39]).

### 5.12 Customer channels (Inferred)
Each customer-hierarchy code is labelled from the share of its customer names containing channel words in English and Arabic (baqala/بقالة, tamweenat/تموينات, aswaq/markets, bufia/matam/restaurant, pharmacy/صيدلية, trading, fuel stations…). Market context: traditional trade 23% of Saudi FMCG sales in 3Q25 ([S31]); baqalahs ~57% of grocery in 2018 ([S32]).

| code | customers | sales_sar_mar_oct_2026 | share_names_with_any_keyword | inferred_channel | confidence |
|---|---|---|---|---|---|
| 10161 | 12276 | 19,602,066 | 70.3% | tamweenat (grocery store) | low |
| 10160 | 3194 | 18,662,711 | 73.6% | mixed / unclear | none |
| 10164 | 2790 | 1,021,297 | 63.2% | HoReCa (food service) | medium |
| 30304 | 223 | 294,879 | 74.9% | pharmacy | medium |
| 10163 | 75 | 255,946 | 56.0% | wholesale / trading | low |
| 10104 | 157 | 186,019 | 58.6% | wholesale / trading | medium |
| 30301 | 80 | 160,099 | 68.8% | wholesale / trading | medium |
| 20213 | 47 | 115,921 | 93.6% | fuel station / automotive | medium |
| 10213 | 86 | 110,252 | 86.0% | wholesale / trading | medium |
| 10103 | 8 | 201 | 75.0% | tamweenat (grocery store) | low |

### 5.13 Receivables ageing (Simulated)
Customer balances are a single snapshot (20 Oct 2026). Assumption: payments clear the oldest invoices first (FIFO), so each credit customer's balance sits on its **most recent** van-sales invoices. Due date from the payment term.

- 1,616 credit customers with a balance: SAR 3,819,782; allocated to 6,011 invoices SAR 3,483,551; **SAR 336,231 cannot be matched** to van-sales invoices in the window — flagged per customer. These are van-sales credit customers only; wholesale receivables (the PoV focus) are not in the data.

| ageing_bucket | open_amount_sar |
|---|---|
| not_due | 2,892,620 |
| 1-30 | 343,217 |
| 31-60 | 145,546 |
| 61-90 | 48,691 |
| 90+ | 53,478 |

### 5.14 Outlets and van routes (Inferred)
- Activity status at 20 Oct 2026: no_purchase_in_window (Mar-Oct 2026) 14,022, active 3,816, lapsed (churn proxy) 662, slowing 483. Churn proxy = no purchase for max(60 days, 3 × the outlet's usual gap).
- Van reps: median **6 drops/day**, median SAR 526 per drop.

### 5.15 Calendar (Researched + validated against Actual)
Events: Ramadan 18 Feb – 19 Mar ([S30]); Founding Day 22 Feb ([S29]); Eid al-Fitr holiday from close of 18 Mar for 4 days, Eid = Fri 20 Mar ([S25], [S26]); Arafat Tue 26 May + Eid al-Adha Wed 27 May ([S27], [S28]); National Day 23 Sep ([S29]). The warehouse data confirms them:

| event | wms_activity_index_excl_fridays | observed_warehouse_closure_days |
|---|---|---|
| Ramadan 1447 | 1.12 | 2026-03-21 |
| Founding Day | 0.42 |  |
| Eid al-Fitr private-sector holiday | 0.39 | 2026-03-21, 2026-03-22, 2026-03-23 |
| Arafat Day + Eid al-Adha private-sector holiday | 0.01 | 2026-05-26, 2026-05-27, 2026-05-28 |
| National Day | 0.11 | 2026-09-23 |

During Ramadan the warehouse ran **~13% above** its normal weekday level.

### 5.16 Opportunity levers (Simulated)
Annual profit impact. Jeddah labour from §1; the four Riyadh levers turn extra sales into profit at a 16% distributor gross margin ([S38]).

| lever | module | confidence | low | base | high |
|---|---|---|---|---|---|
| Van-rep productivity | Retail (van sales) | Low-Medium | 264,727 | 705,938 | 1,235,392 |
| Jeddah labour pace | Warehouse | Medium | 491,647 | 639,485 | 805,806 |
| Price leakage | Retail (van sales) | Low-Medium | 158,697 | 396,743 | 793,485 |
| Returns reduction | Distribution | Low | 106,071 | 353,569 | 692,995 |
| Outlet win-back | Distribution | Medium | 72,516 | 193,375 | 362,578 |

Total: SAR 2,289,110 a year (SAR 1,093,658 – 3,890,257), USD 610k.

---

## 6. What this means for the deck
- **Safe to present (labelled "Simulated")**: recoverable SAR 639k/yr (P10–P90 SAR 492k–806k), ~11 FTE of ~161 people paid, method = "top quartile of your own shifts, same role"; labour per loose case picked SAR 0.77–1.02 vs client prices SAR 0.50–0.80 (a question, not a verdict).
- **Head count**: 202 employee ids scan each month (138 payroll-confirmed); Binzagr's user list shows 269 active people. Present the range, not one number.
- **Headline ask**: 59% of paid time has no scan → attendance by payroll number unlocks the larger opportunity.
- **Don't present yet**: blue-collar % of revenue, EBIT level, cost-recovery rates as prices.

## 7. Limitations
- Paid time: the head count rests on employment spells from the scans (first to last scan day); logins Binzagr's list does not know are paid only on days they scan; nobody who never scans is counted. Sessions < 4 h are not benchmarked.
- Standards come from the same population they benchmark (relative, not engineered); the time-and-motion study Binzagr mentioned would anchor them.
- WMS role is a menu level (tally clerk / storekeeper / manager), not a job title (picker, forklift driver…); 6% of scans come from logins not in the user list.
- Rate card is cost-recovery, not market price; storage revenue excluded; the per-case example rates are a single statement, not a price list.
- Receivables ageing rests on FIFO; channel labels on name keywords.

## 8. Reproduce
Run from `03_Simulated/scripts`, in this order: `parameters.py` → `sim_labour.py` → `sim_pnl.py` → `sim_inferred.py` → `sim_commercial.py` → `sim_opportunity_levers.py` → `sim_checks.py` → `build_sim_report.py`. Input is `../../02_Cleaned/data` (run 02 first, including `clean_mappings.py`) and output is `03_Simulated` (override with `BZ_CLEAN` / `BZ_OUT`; `BZ_RAW` for the name check). Monte Carlo seed 20261021; IsolationForest seed 7. Requirements: pandas ≥ 2, numpy, pyarrow, scikit-learn, openpyxl.
