Commit Graph
55 Commits
Author SHA1 Message Date
mahmoodandClaude Sonnet 5.5 6514b05d18 fix(money,orders): wage/gram-debt parity, atomic document confirm, expert edit, made-to-order, QA fixes
- Rial wage and gram-debt arithmetic matched to the reference; order corrections keep debt in sync
- Document confirm/un-confirm is a compare-and-swap (no double-applied cache under concurrency)
- Settlement document and expert inputs validated (400 instead of 500)
- Expert: edit / activate-deactivate own customers, adjust raw weights only
- Made-to-order flow in the app, PreOrder.quantity migration
- Order grids: customer column, sort keys; panel select search; pre-order quantity save
- Rebuilt release artifacts

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-29 18:09:02 +03:30
mahmoodandCursor 959a272227 build: refresh release artifacts after dropping user-app
Rebuild API bundle, admin panel, and user-app-v2 for Dokploy copy-only deploy.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-28 16:25:46 +03:30
mahmoodandClaude Sonnet 5.5 bc881a0a8a build: rebuild API release bundle for the one-mobile-one-person check
Rebuilds apps/api/release so the deployed image carries 5f3b004. The vendored
Prisma client changes are only its recorded build paths. No source change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 14:26:16 +03:30
mahmoodandClaude Sonnet 5.5 d8b6d36c1b build: rebuild API release bundle and admin panel for the omde list debug block
Rebuilds apps/api/release and apps/admin-panel/dist so the deployed images
carry 0156a97 (X-Debug counts block on the wholesale lists, console output in
the panel). No source change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 09:38:35 +03:30
s.esmaeiliandClaude Opus 5.5 ab39f6abed chore(api): temporary boot-time data diagnostics in the container log
Prints aggregate counts only, prefixed [DIAG], right after migrations: customers
by kind/active/archived/step, what the omde list can show and how many rows are
sales-expert trading accounts (created vs adopted), experts' profile/trading
state, referral counts, the Admin table and the last applied migrations.

Read-only, no mobile/name/id is printed, each query isolated so a failure is
reported without stopping boot. Disable with DIAG_LOG=false; remove once the
empty-omde-list investigation is closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 08:02:57 +03:30
s.esmaeiliandClaude Opus 5.5 ee43d664d5 fix(experts): an expert edited in the panel now shows up in the app
The app shows a sales expert their own account through the linked trading
Customer row (/customer/me). That row was seeded from the expert once, at first
sign-in, and never updated again, so a name, mobile, credit-limit or wage-percent
change made in the panel never reached the app. In the reference the agent and
that account were one row, so an edit changed both.

Editing an expert now mirrors exactly the changed fields onto the trading row in
the same transaction: name parts, credit limit (gram_level), wage percent, and the
mobile when no other customer holds it. Existing drift heals on the next save.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 08:00:01 +03:30
s.esmaeiliandClaude Sonnet 5.5 600ce06d15 fix: Persian duplicate-value errors; admin panel favicon uses the project logo
- PrismaExceptionFilter returned "Unique constraint failed on: mobile" in
  English for a duplicate. It now answers in Persian, naming the field
  (mobile, national code, member code, order number), with a safe generic
  fallback. Covered by a unit test.
- The admin panel still shipped the old "NZ" favicon. favicon.png/.ico are now
  generated from the project logo (the same one the user apps use), in both
  public/ and dist/, and the links carry ?v=2 so browsers drop the cached icon.
- Rebuilt apps/api/release/app.js.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 06:54:28 +03:30
s.esmaeiliandClaude Sonnet 5.5 0c52adcd26 fix(customers): show every non-archived wholesale customer in the main list
The reference (henza_server AdminController::getCustomer) lists type=2 AND
is_archive=0 with no verification or active filter. The develop-branch change
that hid inactive customers with a pending verification step from «عمده»
emptied the list for customers migrated in that state, while their numbers
stayed taken ("already exists"). Restore parity; the sales-expert trading
account exclusion is kept.

Includes the rebuilt release/app.js and a regression test.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 06:50:05 +03:30
s.esmaeiliandClaude Sonnet 5.5 e2ec30f241 build: rebuild API release bundle for the admin/expert table split
app.js, seed.js, the Prisma client in release/vendor and the new migration
20261026120000_split_admin_table, built with `pnpm build:release` (API only;
the admin panel and user apps are unchanged by this work).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 23:55:07 +03:30
s.esmaeiliandCursor 4679e2498c Exclude sales-expert trading accounts from wholesale customer lists.
Expert trading rows are APP_SHOPKEEPER for orders/ledger and were showing next to real omde customers.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-25 23:16:13 +03:30
s.esmaeiliandCursor 066bfd2818 Build release artifacts for deploy.
Refresh API release bundle and admin/user app dist outputs for Dokploy copy-only deploys.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-25 22:46:21 +03:30
mahmood 55d27151c9 feat(products): port missing variant fields from henza and redesign variant modal 2026-10-25 16:06:17 +03:30
mahmood 143d9aefbb fix(cities): remove 'استان' prefix, keep clean city/province names 2026-10-25 13:31:35 +03:30
mahmood 41fc2496ef fix(cities): keep only 31 provinces, remove bulky sub-cities seed 2026-10-25 13:23:57 +03:30
mahmood bb281cc5d6 feat(cities): seed all Iran provinces and cities with tree hierarchy 2026-10-25 13:04:07 +03:30
mahmood af5095a0e3 build: rebuild release and app bundles 2026-10-25 12:08:58 +03:30
s.esmaeiliandCursor 3223c90983 build: rebuild release bundles for orders pagination and dashboard hero
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-17 13:21:55 +03:30
s.esmaeiliandClaude Haiku 5.5 890f5a9725 build: rebuild release bundles with retail dashboard change
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-14 14:54:52 +03:30
s.esmaeiliandClaude Haiku 5.5 c845f6db19 build: rebuild release bundles for build 181
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-14 13:35:35 +03:30
s.esmaeiliandClaude Haiku 5.5 5d2a1efb0c build(api): rebuild release bundle with baseline-first migration fix
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-14 10:46:15 +03:30
s.esmaeiliandClaude Haiku 5.5 6ccdbc06f0 build: release build of admin, user apps and api
Rebuilt dist/ and api release bundle, with the migration for catalog paging indexes.

Fix Less build error: fade(@token, N%) on theme tokens failed in both user-app
and user-app-v2 CustomerChoiceView; use alpha(), the project's token-aware helper.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-12 15:37:03 +03:30
Montech c04505fbd8 Fix some for logout iam 2026-10-11 17:00:31 +03:30
Montech 025d223dac fix some other bugs of iam 2026-10-11 16:01:15 +03:30
Montech 2e21758c2c feat new update from MR S.Esmaieli 2026-10-11 14:43:17 +03:30
s.esmaeiliandCursor 8faf4a7209 build: temporary static super-admin login (09128081392) + api release bundle
TEMPORARY: hardcoded admin login that bypasses the password check and IAM. Remove before going live.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 17:44:25 +03:30
s.esmaeiliandCursor bd3bc09b84 build: montzar brand rename (api release, user-app, user-app-v2)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 17:18:55 +03:30
s.esmaeiliandCursor f124a69e76 build: server bundle with the منتزر SMS texts
Rebuilds apps/api/release from 4d635cb. The apps and the admin panel are unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 17:11:28 +03:30
s.esmaeiliandCursor 48da4cb4c7 build: server bundle and both apps at version 1.142
Rebuilds apps/api/release, apps/user-app/dist and apps/user-app-v2/dist
from b3dae57 (login code without the ~15s wait, no wage-quote loop).
The admin panel is unchanged and not rebuilt.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 16:59:35 +03:30
s.esmaeiliandCursor 840311a2a0 fix(sms): accept a gateway reply with debug text before its JSON
The relay gateway now prints timing lines before its result, e.g.
"1. Function Start at: ...<br>...572381951{"data":"sent"}". The message
was delivered, but the whole body is not JSON, so it was logged as a failed
SMS. When the body does not parse, the trailing JSON object is read instead.
A body with no JSON at all (the gateway's bare PHP errors) still fails.

Tests: sms.service.spec (relayRejection). 33 SMS tests pass.

پیامکِ ارسال‌شده دیگر به اشتباه در گزارش سرور «ناموفق» ثبت نمی‌شود.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 16:39:41 +03:30
s.esmaeiliandCursor 797d030e67 fix(sms): stop sending platform to the relay gateway
With platform "kave" (Kavenegar) the gateway answered "cURL library is not
loaded" and nothing was delivered. The body is now { phone, host, text } and
the gateway picks the line itself. SMS_RELAY_PLATFORM is removed from the
driver, docker-compose and the env examples.

Tests: sms.service.spec updated. 150 tests pass.

پیامک بدون انتخاب خط کاوه‌نگار فرستاده می‌شود و سرویس پیامک خودش خط را انتخاب می‌کند.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 16:34:57 +03:30
s.esmaeiliandCursor c34bf80e6f fix(api): start in production with safe fallbacks instead of crash-looping (502)
A production API with incomplete config threw before listening, so the
container restarted forever and every /api request through nginx was a 502.
The problems are now printed as one loud warning and each gets a safe
fallback:

- JWT_SECRET / JWT_CUSTOMER_SECRET missing, weak or equal: a random key is
  generated once and kept in the Setting table (boot-secrets.ts), so tokens
  survive restarts. A strong value in the environment always wins.
  JwtModule now uses registerAsync so it reads the key after boot fills it.
- OTP_MASTER_CODE: ignored in production unless
  TEST_SERVER_ALLOW_MASTER_CODE=yes.
- CORS_ORIGINS: invalid entries dropped; empty allows every origin.

Tests: boot-secrets.spec, production-checks.spec. 118 tests pass.

سرور با تنظیمات ناقص دیگر مدام ریست نمی‌شود و ورود دوباره کار می‌کند.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-10 16:19:30 +03:30
s.esmaeiliandClaude Opus 5.5 7f851676b5 fix(sms): inside Docker, send the server's public IP as the relay host, not the container's
Messages went out locally but not from the server. The relay gateway
identifies the sending server by `host`, and the owner's ruling (D26)
is that this is the server's IP. With SMS_RELAY_HOST unset, the driver
sent the machine's first external IPv4. On the bare PHP server that was
the real IP; inside the API container it is the container's (172.x).
Reproduced: a container on this machine reports 172.17.0.3.

- relay.driver: when SMS_RELAY_HOST is unset and the machine address is
  private (RFC 1918, CGNAT, link-local), the public IPv4 is looked up
  once (ipify, ifconfig.me, icanhazip in turn) and cached. If every
  lookup fails, it falls back to the old value. SMS_RELAY_HOST still wins.
- SmsService logs the host it will send at boot, and warns when it is
  still private, so a wrong value is visible instead of silent.
- Removed a debug console.log that wrote every recipient's mobile to the
  server log.
- .env.example and SERVER-SETUP-CHECKLIST: SMS_RELAY_HOST = this
  server's public IP.

Tests: relay-host.spec (5). All 254 API tests pass.

اپ: پیامک کد ورود دوباره به‌درستی ارسال می‌شود.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:58:13 +03:30
s.esmaeiliandClaude Opus 5.5 1bc3739e78 Merge release/delivery (IAM single sign-on) into the triage work
Brings the four release/delivery commits (IAM SSO for the admin panel,
both apps and the API; IAM env in docker-compose) onto fix/issues-triage.

Conflicts:
- customer-auth.service.ts imports: both kept.
- both apps' api/client.ts: both kept (AccountingBalance/WageQuote, and
  iamAppUrl).
- all build output (admin-panel, user-app, user-app-v2 dist, and the api
  release bundle): rebuilt from the merged source instead of picking a
  side.

Fixed while verifying:
- The IAM change calls iam.browserBootstrapUrl after every login, but
  the identity-service stand-ins in three test files lacked it. 15 tests
  failed on release/delivery itself.
- SMS relay: SMS_RELAY_PLATFORM was read but never sent, so the gateway
  never got `platform` (documented body: phone, host, text, platform).
  relayConfig() now returns it ("default" → null) and the request carries
  it. sms.service.spec now passes.

All 249 API tests pass; the three front-ends type-check and build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 09:03:53 +03:30
mahmood dcb74603ad feat add some for users in iam 2026-10-08 00:10:10 +03:30
mahmood 40a28d5605 Feat build new 2026-10-07 21:26:54 +03:30
mahmood 148b99c4a6 feat build 2026-09-25 19:12:39 +03:30
s.esmaeiliandClaude Opus 5 24b7a4c395 fix(api): treat blank env vars as unset; verify the whole flow in Docker Compose
Found by running the delivery flow against the actual built container rather
than a local dev process.

DEPLOY-02 (Critical) — ADMIN LOGIN RETURNED 500 FOR EVERY USER IN DOCKER.
docker-compose.yml passes each optional variable as ${X:-}, so an unconfigured
one arrives as "" rather than undefined. The code read them with
`process.env.X ?? 'default'`, which only falls back on undefined, so the empty
string reached jsonwebtoken and it threw

  "expiresIn" should be a number of seconds or string representing a timespan

on every sign. Invisible to every existing suite, all of which run against a
local process where the variable is genuinely absent.

Added src/common/env.ts (envOr / envOrNull), which treat blank as unset, and
applied it to every auth, port, upload and SMS read. Three were not cosmetic:
a blank UPLOAD_DIR would have served static files from the FILESYSTEM ROOT, a
blank PORT would have bound a random ephemeral port (Number('') === 0), and a
blank JWT_SECRET would have become the verifying key. Recorded as D25, with a
standing rule to use these readers instead of `??`.

Pinned by src/common/env.spec.ts (9 tests), which reproduces the original throw
against the real jsonwebtoken and asserts the reader can no longer produce it.

DEPLOY-03 (process, documented) — the API image is `COPY release/ ./`, so it
ships a bundle pre-built on the developer machine. `docker compose build` will
happily produce an image from a STALE release/app.js and report success; at the
start of this run that bundle was four days old and the rebuilt container was
missing every route added since. DELIVERY.md's deploy section now leads with
`pnpm build:release`.

New: scripts/test-compose-e2e.cjs — the whole delivery flow against the built
container, over HTTP only, as a black box: a sales expert and an ordinary
customer both sign in with the master code, both place an order, both orders
appear in the admin grid and open there, and the three token boundaries still
hold in the deployed image. 32/32.

Verified: compose e2e 32/32 · jest 54/54 · build 5/5 ·
against the CONTAINER: complete-profile 38/38, report 40/40, expert login 36/36 ·
locally: apps smoke 43/43, admin smoke 43/43, B2B scan 34 ok/0 broken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 02:27:30 +03:30
Montech 79226c9f13 fix some for api download pic 2026-09-19 15:44:00 +03:30
s.esmaeili 7227e9c61b build 2026-09-19 13:12:58 +03:30
s.esmaeili 80ccf1a2aa feat build 2026-09-18 19:58:29 +03:30
mahmood 062fe80a68 feat build 2026-09-18 17:30:32 +03:30
Montech 05fe780c89 Merge branch 'dev'
# Conflicts:
#	apps/admin-panel/dist/index.html
#	apps/admin-panel/nginx.conf
#	apps/admin-panel/vite.config.ts
#	apps/api/release/app.js
2026-09-16 15:45:23 +03:30
mahmood 4743cfb89f fix admin and montzar 2026-09-16 11:59:35 +03:30
mahmood 2448627e1b fix: stop drift migration from dropping tracking table 2026-09-15 22:40:00 +03:30
Montech 341c7a5095 fix some for db 2026-09-15 17:25:12 +03:30
Montech a7e13fe64e fix update build 2026-09-15 15:41:47 +03:30
s.esmaeiliandClaude Opus 5 a12fb0450e api: don't replay the baseline migration on pre-fix databases
The folder-discovery fix in the previous commit surfaced a second problem.
Databases migrated BEFORE that fix recorded the baseline under the name of
the hand-added flat duplicate that used to sit beside it
(prisma/migrations/0001_init.sql). Once the runner started discovering the
real folder (20260910113720_first), it saw a name it had never recorded and
tried to apply the baseline again — against a database that already had it:

  Raw query failed. Code: 42710.
  ERROR: type "AdminRole" already exists

The two files are identical SQL (verified byte-for-byte after normalising
line endings; they differ only by one trailing blank line), so the recorded
legacy name genuinely means that migration is applied. A LEGACY_NAME_ALIASES
map now treats '0001_init.sql' as an already-applied marker for
'20260910113720_first', so upgrading an existing database is a no-op instead
of a crash.

Verified against the local dev database this actually broke on
(postgresql://localhost:5432/gold_banking, which had recorded only the old
flat name): 'npm run dev' in apps/api now logs 'No pending migrations' and
'Nest application successfully started' instead of dying. That database's
tracking table was also reconciled to record both real migration names, and
it was confirmed to already contain both migrations' schema (AdminRole,
City.percent, user_type, CustomerKind all present) — nothing was re-run
against it. The dockerised database is unaffected: it already records the
real names, so the alias is inert there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 21:44:14 +03:30
s.esmaeiliandClaude Sonnet 5 2b6795d007 api: fix migration runner, serve uploads correctly, add product photos
Three real bugs found and fixed while wiring up product images end-to-end:

1. src/prisma/migrate.ts only discovered flat prisma/migrations/<name>.sql
   files, never Prisma's own folder-per-migration layout
   (<timestamp>_<name>/migration.sql). Every migration created the normal
   way (via 'prisma migrate dev') was silently skipped; only a hand-added
   flat duplicate (prisma/migrations/0001_init.sql, now removed) ever ran.
   This meant schema.prisma had drifted well ahead of what any real deploy
   database actually had (missing columns on City, Customer, Branch,
   Product, several new tables). Fixed to discover both layouts; the
   drift is closed with a new 20260914165255_sync_schema_drift migration.

2. Neither nginx.conf (production) nor vite.config.ts (dev) proxied
   /uploads/ to the API — only /api/. UploadedFile/ProductImage URLs
   (/uploads/<file>, served by main.ts's useStaticAssets OUTSIDE the /api
   prefix) 404'd through both. Both now proxy /uploads/ the same way /api/
   already was.

3. docker-compose.yml's api service had no volume for storage/uploads, so
   every uploaded file — and by extension every ProductImage row pointing
   at one — was lost on the next container recreate/redeploy. Added a
   named volume.

Also: main.ts's static file serving now sets Content-Type: image/avif
explicitly (serve-static's bundled mime table predates .avif and served it
as application/octet-stream; browsers rendered it anyway by sniffing, but
a stricter client would not have).

Product photos: copied 15 real jewelry photos into the uploads volume,
registered them as UploadedFile rows, and assigned each of the 5 existing
Product rows a randomly picked one (duplicates allowed, per request),
replacing their /img/no-items.png placeholder ProductImage row. Verified
end-to-end in a real browser against the real containerized API+DB: logged
in, opened the admin product list, confirmed all 5 rows render their photo
(naturalWidth/Height populated, network 200s, no console errors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 20:56:17 +03:30
mahmood f19f49daeb feat: auto-run SQL migrations and one-time seed on boot 2026-09-10 17:15:17 +03:30
mahmood 1b675804ea fix: nginx dns re-resolve, db healthcheck, explicit bind 2026-09-10 14:51:57 +03:30