Skip to content

feat(sales): sales may change the deposit % on a draft notice; the amount follows - #1176

Merged
waritctd merged 18 commits into
developfrom
feat/deposit-percent-editable-on-draft
Oct 27, 2026
Merged

waritctd merged 18 commits into
developfrom
feat/deposit-percent-editable-on-draft

Conversation

@waritctd

Copy link
Copy Markdown
Owner

Draft. Owner ruling (Ploy, 2026-10-23): sales can change the deposit % on the deposit notice and the amount recalculates automatically — on a DRAFT only; an issued notice is a customer document (409). This REVERSES the 2026-09-29 ruling that locked the % to the quotation. The % still defaults from the quotation at creation (creation paths untouched; #1170 reworks them). One deposit per deal stays (already enforced: issuing supersedes older ISSUED notices, DepositNoticeRepository:304, pinned by DepositNoticeIssueGuardIntegrationTest#issue_secondDraftOnSameTicketSucceedsAndSupersedesTheFirst).

Change

  • DepositNoticeService#update: honours depositPercent on a draft (null keeps the stored one); refuses ≤ 0 ("เปอร์เซ็นต์มัดจำต้องมากกว่า 0") and non-whole percents ("เปอร์เซ็นต์มัดจำต้องเป็นจำนวนเต็ม") with 400; issued → 409 via requireDraft. @DecimalMax message now Thai ("เปอร์เซ็นต์มัดจำต้องไม่เกิน 100").
  • DepositNoticeRepository#update: recomputes deposit/VAT/total from the new percent; latent bug fixed: an update without items (null or []) used to zero the subtotal and amount while keeping the rows — it now sums the stored rows.
  • DepositNoticePage.jsx: enabled whole-number % field on a draft (typed value rounded), live recalculation, sends depositPercent; read-only once issued; cleared field shows ฿0 (was a silent 50%).
  • Mock depositNotices.update mirrors draft/issued/zero/fractional/over-100 rules. Stale "locked" comments rewritten.

Test-first (each test commit precedes its fix commit; RED quotes)

Test Pins Red before
DepositNoticeServiceTest#update_onADraft_persistsTheCallersDepositPercent (revised from the old "ignoring caller value" pin) 0.40 reaches the repository expected: 0.40 but was: 0.50
…#update_withNoDepositPercent_keepsTheDraftsStoredPercent null keeps 0.50 guard (green before)
…#update_refusesAZeroOrFractionalDepositPercent_andWritesNothing 0 and 0.405 → 400, nothing written Expecting code to raise a throwable
DepositNoticeControllerTest#updateRejectsADepositPercentOver100WithAThaiMessage 1.5 → Thai message English message
DepositNoticeIssueGuardIntegrationTest#update_onADraft_changesThePercentAndTheAmountFollows (real DB) DTO 0.40 / 40.00 / VAT 2.80 / 42.80; preview "ขอรับเงินมัดจำ (40%)"; then 0.30 with items=null and 0.20 with items=[] keep the rows and recompute expected: 0.40 but was: 0.5000; :261 expected: 100.00 but was: 0.00
…#update_onAnIssuedNotice_refusesThePercentEditWith409_rowUnchanged 409, status/percent/amount/items unchanged guard
DepositNoticePage.test.jsx "lets sales change the deposit percent on a draft…" (revised from the read-only-label test) field enabled, 39.6 → 40, (40%) / 200.00 / 214.00, saves 0.4; cleared → 0.00 Unable to find a label … % มัดจำ; 200.00 บาท (billed 198.00)
… "shows the deposit percent read-only once the notice is issued" disabled, shows 30 —
mockApi.depositNotices.test.js update cases (revised) draft takes 0.4 and recomputes; 0 / 1.5 / 0.405 refused; issued → 409 promise resolved … instead of rejecting

Mutations (implementer 10 + reviewer 5), each killed by a named test: override restored; draft check removed; amount not recomputed; VAT/total stale; zero allowed; whole-number check off; Thai message off; page rounding off; || 0.5 fallback restored; field always disabled; page sends ×100; mock checks off; stored-rows sum replaced by empty stream.

Not covered: decimal input beyond rounding (field is whole-number); concurrent updates (pre-existing DELETE/INSERT race); the creation paths (#1170).

Opus review (2026-10-23): APPROVE WITH NOTES — all notes applied

Medium (fractional % → mismatched document) fixed in ce592248. Checked OK by reading: remaining invoice (DepositNoticeService:689/736), confirmDepositPaid (TicketService:978), finance view and PDF all read the stored amounts of the ISSUED notice; rounding matches createDraft; no caller sends [] meaning "clear"; complements #1173's issue guards.

Behaviour changes to note: a draft stored at 0% (legacy route) now gets 400 on save until the % is raised (consistent with "0% = no deposit stage"). Pre-existing, left alone: requireDraft runs before the owner check on update (same as issue()); item DELETE/INSERT race.

Merge-tree clean vs develop, #1168, #1170, #1173.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2

@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
demo-glr Ready Ready Preview Oct 27, 2026 9:08pm UTC

Request Review

@waritctd

Copy link
Copy Markdown
Owner Author

/code-review max (head ce59224): 10 finders → 22 unique candidates → 21 confirmed / 1 plausible, + 5 from the sweep. Verdict: do NOT merge yet. Items 1, 6–12, 14, 15 are fixable here; 2, 4, 5 need an owner ruling. Full verdicts in the session scratchpad review-1176/.

Money / document correctness

  1. (PR-introduced) DepositNoticeService:166 the >0 rule lives only in update(). A 0% DEAL_DIRECT quotation (ไม่รับมัดจำ) still mints a 0.00 draft via the ticket route; the editor's only exit is typing a non-zero % → issue() re-checks neither percent nor amount → a deposit is issued against a no-deposit quotation (impossible on base). Fix at the choke points: refuse a 0% source in createDraftFromQuotation (mirror OrderConfirmationService:483) and require 0 < % ≤ 1 and amount > 0 in issue().
  2. (owner call) DepositNoticeService:142 re-creating a draft re-seeds the % from the quotation; on a PCR deal the create button stays visible after a 40% notice is ISSUED, so a re-create + issue silently supersedes the customer's 40% document with a 30% one. Fix: seed from the latest ISSUED notice, or block/warn when one exists.
  3. DepositNoticePage.jsx:461/468 the || 0 fix on the % was not applied to the identical netUnitPrice || unitPrice || 0 on line net prices: a 100%-discounted line is summed and SAVED at its unit price by the first PUT, which editing the % now requires. ?? as lines 766/770 already do.
  4. (owner call, pre-existing) DepositNoticeRepository:430 findDealQuotationItems copies DEAL_DIRECT ADJUSTMENT rows (qty −1) into the draft; @PositiveOrZero qty then rejects every PUT from the page → on any deal with ส่วนลดพิเศษ nothing in the editor saves and the % edit is unreachable. Fold the adjustment into goods lines, or allow signed qty end-to-end.
  5. (owner call, pre-existing) DealDepositPanel.jsx:159 one-click ออกเอกสาร with no confirm and no baht amounts; no re-version/void for an ISSUED notice. A saved typo (3 for 30) becomes a permanent numbered document. ConfirmDialog with amounts + a re-version path.
  6. (PR-introduced) DepositNoticeService:165 the removed override was the only stale-request protection: two tabs on one draft, B saves after A changed the % → B's old % and amounts win silently (page reseeds only on id:version:status; version never changes). Pattern exists: LeadTimeChangeRepository:113 AND version = :expectedVersion.
  7. DepositNoticeRepository:244 update() ignores the header UPDATE row count (WHERE status='DRAFT') and then runs item DELETE/INSERT unguarded → a save racing issue() rewrites the ISSUED notice's lines behind a 200, and the PDF/XLSX totals are template formulas over the rows (not reads of the stored amounts, as the Opus note said). Check the count → 409 before touching items.

Validation / UX
8. DepositNoticePage.jsx:452 a cleared % sends 0 → 400 rejects the WHOLE PUT incl. customer/item edits (page contract says partial saves must work). Send null for '' or validate client-side; filter blank เพิ่มรายการ rows.
9. DepositNoticePage.jsx:103 validateIssueForm never sees the %: 0/negative blames unit price/qty; 150% passes the gate, opens the confirm dialog, then fails with an English-prefixed toast and the dialog stays open. Add a whole 1–100 rule with its own message.
10. DepositNoticeService:170 whole-number check runs on the echoed stored value: a draft stored at 0.3330 (pre-09-29 free input) shows 33 but 400s on every save. Skip when req == doc or round in populateForm; enforce in issue() too.
11. DepositNoticeDraftRequest:16 one rule over four mechanisms; @DecimalMin("0") still English for −5. @DecimalMin(value="0", inclusive=false, message="เปอร์เซ็นต์มัดจำต้องมากกว่า 0") (idiom: ThicknessDefaultRequests:28).
12. DepositNoticeService:161 update() has no requireRole(SALES_ROLES) while create/issue do; a grant-revoked or promoted owner can still PUT 100%. Add the role gate; fix the stale "sales_manager can never own a ticket" comments in the test.
13. mockApi.js:13347 mock update: checks run after 404/409 (backend @Valid runs first), skips >0 on the stored value, no owner check on update/issue.
14. DepositNoticeIssueGuardIntegrationTest:306 the stored-lines branch is tested with one line qty 1 unit=net=amount=100 (cannot tell net×qty from unit×qty or SUM(amount)); mock reread weakened in 50b2bd6; "cleared field bills 0" assertions sit after a line that goes red first so never ran red; two mock its labelled "(revised)" are new.
15. Cleanup DepositNoticeRepository:213 dead null → 0.50 fallback; comments claiming "defaults from the quotation" are false for a NULL-% quotation (#1170's area); page money math drops per-step HALF_UP (฿0.01 drift in the summary/confirm dialog); findItems re-reads rows already held.

Cut at the 15-cap (pre-existing): PricingRequestDetailPage 0–1 "% มัดจำ" input discarded (typing 40 → English 400); header-subtotal/row precision for 4-dp quantities.

waritctd and others added 16 commits October 28, 2026 01:07
…e and the amount follows (RED)

Owner ruling 2026-10-23 reverses the 2026-09-29 lock. Red until DepositNoticeService.update
stops overriding the percent, refuses 0, and the page offers an editable field on a draft.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…ount follows

Owner ruling 2026-10-23 reverses the 2026-09-29 lock. DepositNoticeService.update now persists the
request's percent (null keeps the draft's own, 0 or less is refused with 400); requireDraft still
409s an issued notice. The repository already recomputes the amount from the percent on update.
DepositNoticePage shows the % as a whole-number field on a draft, recalculates the amount as sales
types, and is read-only once issued. The mock refuses 0 the same way.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
… stored lines and their amount (RED)

Today a null or empty items list saves subtotal 0 and deposit 0 while the item rows stay.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…nes and recomputes the amount

DepositNoticeRepository.update replaces item rows only when items are sent, but computed the
subtotal from the request alone, so a null or empty items list saved subtotal 0 and deposit 0
beside intact rows. It now sums the stored rows in that case. Null and [] both mean keep: rows
were never deleted on [], so the safer reading leaves them and makes the money match them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…ets a Thai message, and a cleared field bills 0 (RED)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
… message; the page rounds what sales types

DepositNoticeService.update refuses a request percent that is not a whole number of percent (400),
because the document prints it rounded: 40.5% showed as 41% while billing 40.5%. The @DecimalMax
on DepositNoticeDraftRequest.depositPercent carries a Thai message instead of the English default.
DepositNoticePage rounds the typed value so the field, label and amount agree. The mock refuses
over 100% and fractional percents the same way.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…ice request; the repository update takes the stored lines it was handed

Inert plumbing for the next commits: DepositNoticeDraftRequest gains a nullable
expectedUpdatedAt (an 8-argument constructor keeps every existing caller compiling),
read by nothing yet. DepositNoticeRepository#update now sums the lines the service
already read (doc.items()) instead of re-reading them; same rows, one query fewer.
No behaviour change: DepositNoticeServiceTest 50, DepositNoticeControllerTest 9,
DepositNoticeIssueGuardIntegrationTest 5, all green.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…sued-row refusals, legacy % echo, Thai % floor, sales-only update (RED)

RED before the fix (each an assertion on the missing behaviour):
- issue refuses a stored 0% / 0.3330 / 1.5 draft and a 0-baht draft, nothing numbered:
  DepositNoticeIssueGuardIntegrationTest:221 "Expecting code to raise a throwable"
- a save with a stale updatedAt stamp is 409 and the first save stands:
  DepositNoticeIssueGuardIntegrationTest:301 "Expecting code to raise a throwable"
- DepositNoticeRepository#update on an ISSUED row refuses before touching the lines:
  DepositNoticeIssueGuardIntegrationTest:334 "Expecting code to raise a throwable"
- a stored 0.3330 sent back unchanged saves: DepositNoticeServiceTest:1084
  ApiException "เปอร์เซ็นต์มัดจำต้องเป็นจำนวนเต็ม"
- a sales_manager that owns the deal cannot update: DepositNoticeServiceTest:820
  "Expecting code to raise a throwable"
- -0.05 and 0 get a Thai message: DepositNoticeControllerTest
  expected "depositPercent เปอร์เซ็นต์มัดจำต้องมากกว่า 0" but was "must be greater than or equal to 0"
Guards (green before): the stamp binds through the canonical record constructor; the
stored-lines fixture now has two fractional-qty lines with net below unit, so net x qty
(75.02), unit x qty (90.00), the rounded amounts (75.03) and nets alone (43.34) differ.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…issue save is refused before the lines; update is sales-only

- DepositNoticeService#issue: a stored percent outside a whole 1-100 (0%, 0.3330, 1.5) is 409
  before numbering; a 0-baht draft is 409 (same lines as #1173, so the two merge clean).
- DepositNoticeRepository#update: honours expectedUpdatedAt (AND updated_at = :expected, null
  skips it) and checks the header row count; 0 rows -> 409 "ถูกแก้ไขโดยผู้ใช้อื่น" while still a
  draft, else requireDraft's message, both before the item DELETE/INSERT. Dead null -> 0.50
  fallback removed (the service always sets the percent).
- DepositNoticeService#update: requireRole(SALES_ROLES) first, like create/issue; the
  whole-number rule skips a stored percent sent back unchanged.
- DepositNoticeDraftRequest: @DecimalMin exclusive with a Thai message.

GREEN: DepositNoticeServiceTest 50, DepositNoticeControllerTest 10,
DepositNoticeIssueGuardIntegrationTest 6. Mutations, each red on its named test: % re-check
narrowed to signum, amount check off, stamp predicate dropped, row count ignored, legacy-echo
skip removed, role gate removed, @DecimalMin back to inclusive English, stored-lines sum
switched to unitPrice x qty.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…d the folded ส่วนลดพิเศษ row (RED)

Owner rulings 2026-10-23 (items 2 and 4 of the #1176 review):
- while a notice is ISSUED, the ticket route and the pricing-request route both refuse a new
  draft with 409 naming that notice, and no row is written:
  DepositNoticeIssueGuardIntegrationTest:259 "Expecting code to raise a throwable"
- a DEAL_DIRECT quotation's ADJUSTMENT row is folded into the goods lines by amount: 2 lines,
  nets 95.00 / 475.00, subtotal 1,900, and the page-shaped PUT validates and saves:
  DealQuotationConfirmOrderIntegrationTest:262 expected ("10","100.00","95.00",...) but the draft
  held ("10","100.00","100.00"), ("2","500.00","500.00"), ("-1","100.00","100.00")
Revised for the new rule: PaymentTrackIntegrationTest's revision walk now shows the create
route refusing (PaymentTrackIntegrationTest:288, RED) and writes the revision draft through the
repository, as the separate re-version change will; RemainingInvoiceServiceIntegrationTest's
second-chain fixture does the same (green before and after, 28 tests).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…he ส่วนลดพิเศษ row into the goods lines

- DepositNoticeService#createDraftFromQuotation (both the ticket route and
  OrderConfirmationService#createDepositNoticeFromQuotation funnel here): while a notice is
  ISSUED, 409 "ดีลนี้ออกใบแจ้งรับมัดจำเลขที่ <no.> แล้ว จึงสร้างฉบับร่างใหม่ไม่ได้".
  issue()'s DEPOSIT_NOTICE_ISSUED self-loop stays for the separate re-version change.
- DepositNoticeRepository#findDealQuotationItems: a DEAL_DIRECT ADJUSTMENT row (qty -1) is no
  longer copied; foldAdjustments spreads its amount over the goods lines by amount (HALF_UP per
  share, remainder on the last line with an amount), keeps the descriptions and relabels the
  per-unit discount. Subtotal unchanged for the review's fixture (1,900); a qty that does not
  divide a line's new amount can move it by under qty x 0.005 (ponytail comment).

GREEN: DepositNoticeIssueGuardIntegrationTest 7, PaymentTrackIntegrationTest 14,
DealQuotationConfirmOrderIntegrationTest 31, DepositNoticeServiceTest 51.
Added with the fix (proved by mutation, not RED-first): DepositNoticeServiceTest
#foldAdjustments_putsTheRoundingRemainderOnTheLastLineWithAnAmount, since the IT's 50/50
split cannot tell a remainder from a plain proportional share.
Mutations, each red on its named test: guard filter -> false; fold skipped; remainder off.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…hecks, one notice per deal in the UI, and a confirm before issuing from the deal (RED)

RED before the fix:
- DepositNoticePage: a stored 0.333 saves as 0.33; a net-0 line adds 0 and is sent at 0
  (Unable to find "100.10 บาท": the line fell back to its unit price); 100.10 at 33% shows
  35.34; a blank เพิ่มรายการ row is dropped; the stamp travels; a cleared % sends null
- DepositNoticePage: a 409 save reloads the draft and toasts the server message (payload had
  no expectedUpdatedAt)
- DepositNoticePage: % 0 and 150 are blocked on the % field before the dialog (Unable to find
  "กรุณากรอก % มัดจำเป็นจำนวนเต็ม 1–100")
- mock depositNotices: @Valid-shaped messages before the 404/409 lookups ("ไม่พบใบแจ้งรับมัดจำนี้"
  instead of 400), the reread straight after the three rejects, the stale stamp, the owner/role
  checks on update and issue, no second draft on either route ("promise resolved instead of
  rejecting")
- mock promoteToDeal: the deposit draft folds a flat ส่วนลดพิเศษ (got [10, 850, 850, null] and a
  qty -1 row)
- canCreateDepositNoticeFromQuotation refuses once an ISSUED notice exists (expected true to be false)
- PricingRequestDetailPage step 6 points at the ISSUED notice (Unable to find link /GLRD69001/)
- DealDepositPanel (TicketDetailPage): ออกเอกสาร opens a dialog with %, deposit, VAT, total
  (Unable to find role="dialog"); an ISSUED notice says the deal already has its notice

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…s; one notice per deal in the UI; the deal page confirms before issuing

DepositNoticePage: a net-0 line counts as 0 (?? not ||); the stored % is rounded to the whole %
the field shows; money rounds per step (round2, now exported from quotationMeta); a blank
เพิ่มรายการ row is left out of the save and the issue checks; a cleared % sends null; the
draft's updatedAt goes as expectedUpdatedAt and a 409 reloads the draft and toasts the server
message; the issue gate has its own whole 1–100 % rule focused on the % field; the confirm
body is DepositIssueSummary, shared with DealDepositPanel.
DealDepositPanel: ออกเอกสาร opens a ConfirmDialog with the stored %, deposit, VAT and total; an
ISSUED notice says the deal already has its notice. canCreateDepositNoticeFromQuotation takes
the deal's notices and refuses once one is ISSUED; PricingRequestDetailPage step 6 reads them
(existing listByTicket) and links the ISSUED number instead of offering the create.
Mock: @Valid-shaped % checks before the lookups, sales-only + owner on update and issue, the
stored-% floor, the legacy echo, the stale stamp (updatedAt strictly increases), issue's %
and amount checks (amount lines as in #1173), no new draft on either route while one is
ISSUED, and the ส่วนลดพิเศษ fold for a flat adjustment.

GREEN: DepositNoticePage 15, mockApi.depositNotices 13, mockApi.promoteToDeal 11,
pricingRequestMeta 75, PricingRequestDetailPage 277, TicketDetailPage 169, contract +
serverContract + quotationMeta. ESLint clean. Mutations, each red on its named test: ?? -> ||,
stamp dropped, 409 reseed dropped, cleared % -> 0, blank-row filter off, % rule off,
populateForm rounding off, all per-step rounding off, mock owner check off (update and issue),
mock validation moved after the lookups, mock stamp off, mock reject leaks the %, mock
one-notice guard off, mock fold off, predicate ignores notices, PR page ignores notices, panel
line removed, panel issues without the dialog.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…from #1170/#1173's hunks

No behaviour change: requireNoIssuedDepositNotice now sits above buildMockDoc (it was adjacent
to #1170's rewrite of mockPickTicketQuotationForDepositNotice), and the new predicate and
step-6 tests open their describe blocks instead of sharing #1173's insertion points.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…import does not collide with #1170's

No behaviour change (DepositNoticeIssueGuardIntegrationTest 7, DepositNoticeServiceTest 51,
DepositNoticeControllerTest 10 green with clean).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UXmTJDVga17Rn5az4YBjZ2
…al is issued; 2-decimal lines; the quotation's own discount wording kept

Owner rulings 2026-10-28 on the #1176 review:
- Item 1(a): DepositNoticeService.issue refuses while another notice on the deal is ISSUED
  ("ดีลนี้ออกใบแจ้งรับมัดจำเลขที่ … แล้ว จึงออกฉบับร่างนี้ไม่ได้"), so a second draft can no longer
  replace the document the customer already has. Mock mirrors it.
- Item 2 ("use 2 decimals for all"): foldAdjustments rounds each share and each new net price to 2
  decimals with no remainder pushed onto a last line; the notice total is the sum of its own 2-dp
  lines and may differ from the quotation by a few satang (documented as the ruled outcome).
- A line keeps the quotation's own discount wording and gains "+ ส่วนลดพิเศษ … ต่อหน่วย" only when
  the fold lowered its price.
- The rebase onto develop left a duplicate requireNoIssuedDepositNotice in mockApi.js; one removed.

Tests: issue_secondDraftOnSameTicket_…StillRefusedWhileAnotherNoticeIsIssued (IT; red with the guard
disabled, green with it), depositNoticeSecondIssue_isRefused_… (PaymentTrack IT),
foldAdjustments_keepsEachLinesOwnLabel_…TwoDpLines (unit), mock deposit-notice tests. Green:
DepositNoticeServiceTest 50, DepositNoticeIssueGuardIntegrationTest 7, PaymentTrackIntegrationTest
14 (local Postgres), mockApi.depositNotices + deposits 36.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@waritctd
waritctd force-pushed the feat/deposit-percent-editable-on-draft branch from ce59224 to 4d92a57 Compare October 27, 2026 19:14
…e locks the deal and takes an optional stamp, page refuses cleared rows and keeps stored %

- DealDepositPanel: notices were landing in the accepted-quotation slot; pass the deal's quotation (0% hides create) and notices separately.
- DepositNoticeService.issue: lock the ticket row before the one-ISSUED guard; optional expectedUpdatedAt (409 if the draft moved on). Dead SUPERSEDE update removed.
- DepositNoticePage: save/preview refused when every row is cleared; a stored non-whole % is no longer rewritten; issue sends the stamp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…de and remainder rounding

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@waritctd
waritctd marked this pull request as ready for review October 27, 2026 21:19
@waritctd
waritctd merged commit a143f8a into develop Oct 27, 2026
7 checks passed
waritctd added a commit that referenced this pull request Oct 27, 2026
…-to-main

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — a8512a61 Deployed Oct 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant