| Rev |
Age |
Author |
Path |
Log message |
Diff |
| 37853 |
1 d 0 h |
amit |
/trunk/profitmandi-dao/src/main/resources/sql/ |
docs(warehouse): record-only migration files for 2026-10-05 HR move - suspended-warehouse brand mapping (1:1 HR substitution, 1,062 deleted / 51 added), 3 franchise stores 7678->13372, note for the store remap and pending-order move done outside the code (all already applied on hadb1, do not re-run) |
|
| 37850 |
1 d 0 h |
amit |
/trunk/profitmandi-dao/src/main/ |
refactor(warehouse): retire hardcoded Delhi ids - allocation fallback vendor warehouse 7573 -> 13368 (central HR-NSSPL/GGN); Vivo Noida/Ghaziabad partner-sale rule 7573 -> 13370; drop unused InventoryWarehouse WH_DL/WH_HR_DL/WAREHOUSE_IDS; migration 20261005_hr_ggn_central_brand_mapping.sql (13368 central for every ACTIVE warehouse, all brands except Oppo/Vivo, 532 rows, applied on hadb1) |
|
| 37845 |
1 d 1 h |
amit |
/trunk/profitmandi-dao/src/ |
feat(warehouse): ACTIVE/SUSPENDED/INACTIVE status + label on sellerwarehouse replace WAREHOUSE_MAP - BillingWarehouseService (active for dashboards, active+suspended for warehouse screens, names in every state, 5 min cache); setup service: guarded status change (no suspend while franchise stores or brand mapping route there, no close while stock/unshipped orders), rename, all-warehouse overview with usage; partner store assignable only to an active warehouse; daily stock alert for suspended/inactive warehouses to logistics/accounts top 2 staffed levels + leadership; dashboard warehouse list = active (replaces r37834 stock-holding loop); migration sellerwarehouse_status_display_name_20261005.sql (applied on hadb1) |
|
| 37835 |
2 d 6 h |
amit |
/trunk/profitmandi-dao/src/main/resources/sql/ |
chore(sql): record applied migrations - HR movement HSN/GST-rate/category fixes (2026-10-02) and HR warehouse self brand mapping (2026-10-04); both re-runnable, already applied on hadb1 |
|
| 37822 |
4 d 23 h |
amit |
/trunk/profitmandi-dao/src/ |
feat(warehouse): physical warehouse setup service, supplier mapping PO guard, address active flag
- WarehouseSetupService + WarehouseSetupValidator: create seller (state from GSTIN,
unique label/GSTIN), address master (city stored last, 6-digit pin, contact),
physical warehouse (OURS/BAD vendor 40 + Dummy/G + sellerwarehouse + mapping in
one transaction), activate/deactivate (sellerwarehouse.is_active), remap address
with internal store sync. Limits follow column sizes (sql_mode is empty).
- WarehouseAddressMaster.active: only active addresses are offered for assignment.
Migration sql/20261001_warehouseaddressmaster_active.sql (applied on hadb1).
- PO guard: createPurchaseOrder refuses a supplier with no OURS/GOOD vendor
warehouse at the destination (GRN could never receive it).
InventoryWarehouseRepository.hasGoodSupplierWarehouse / selectSupplierIdsWithGoodWarehouse
use existence checks; 67 pairs carry duplicate OURS/GOOD rows.
- SellerService.syncInternalStoreAddress(store); RetailerServiceImpl syncs an
INTERNAL store's address when it moves warehouse or becomes internal.
- Record of 2026-10-01 HR Gurugram setup SQL (seller 21, warehouses 13368/13370/13372).
- Tests: WarehouseSetupValidatorTest (10), WarehouseSetupServiceImplTest (7). |
|
| 37805 |
7 d 16 h |
amit |
/trunk/profitmandi-dao/src/main/ |
fix(catalog): trim f_ colour prefix space; age delisted listings from last activation
- Item.normalizeColor drops the space after f_ on every write (upload sheets typed
'f_ Black', which read ' Black' everywhere); ItemLoaderService uses it so the
duplicate-colour check compares the clean value
- tag_listing.last_activated, stamped by TagListing.setActive on off->on;
updateActiveById now goes through the entity (+flush) so the toggle stamps it
- delist cron + SQL script age a listing from GREATEST(start_date, last_activated),
so a re-activated listing is not delisted again the next morning
- migrations: add_tag_listing_last_activated_20260929.sql, fix_item_color_prefix_space_20260929.sql
(both applied on hadb1 2026-09-29; 174 items trimmed, 36830 excluded - clashes with 37089) |
|
| 37804 |
7 d 19 h |
amit |
/trunk/profitmandi-dao/src/main/ |
fix(fofo-store): store every GSTIN in upper case at the entity setter
FofoStore.setGstNumber now normalises through StringUtils.normalizeGstNumber,
so no write path can store a mixed-case GSTIN. Only the partner-update path
normalised before; the LOI and trial-form paths wrote the field directly,
which is how 06bmlpk3545g1z0 and 03AAccn9802g1zp reached the table. NIC
expects upper case, and case-sensitive comparisons (including the own-seller
GSTIN check behind a NIC refusal) silently failed to match.
uppercase_fofo_store_gstin_20260928.sql corrects the four rows already on
record; it is already applied on hadb1 (0 mixed-case GSTINs remain). |
|
| 37797 |
8 d 3 h |
amit |
/trunk/profitmandi-dao/src/main/ |
Stop a settled debit note being received a second time
A return settled before the receive workflow (first purchase_return_order
2026-03-16) wrote the warehouse SALE_RET scan, a returnorderinfo row and a
wallet refund, but never touched the debit note: it stayed CREATED with no
purchase_return_order. Those are exactly the two things the receive screens
read, so 5,557 fully settled notes looked like they were still awaiting
receipt, Receive button included.
Receiving one of them (DN UPBLY975/4, IMEI 864973083197734, Antu Enterprises)
restored to the partner a phone that had already been returned, refunded
Rs 38,999 and resold to another partner, and a duplicate debit note followed
that Finance could not refund.
- receiveDebitNoteItems: refuse a note that already has a return order, one
whose status is not CREATED, and one whose every unit is already back at the
warehouse. Checked BEFORE the condition-mismatch gate - a mismatch there
returns into rejectOnConditionMismatch without reaching any later check, and
acknowledging that rejection hands the stock back to the partner. That is
how the Antu case slipped through.
- getAlreadyReturnedDebitNotes: the last of those checks, batched for a page of
notes. Mirrors applyReceipt - SALE_RET/DOA_IN/SALE_RET_UNUSABLE for the unit
against the order its invoice was billed on - so a screen can never disagree
with what the refund step will accept.
- processInvoiceReturn: one open return per document.
- sql/backfill_debit_note_status_old_flow_20260924.sql: APPLIED on hadb1
2026-09-24. 4,154 notes -> APPROVED, 6,639 items -> RETURNED, on the same
evidence (returned and refunded). Open notes 5,557 -> 1,404; the rest are
left CREATED for review, bucketed in fofo._dn_backfill_20260922. |
|
| 37790 |
8 d 5 h |
amit |
/trunk/profitmandi-dao/src/main/ |
Return refund as B2C when NIC refuses the buyer's GSTIN
A return whose credit note NIC refuses because the BUYER's GSTIN is
cancelled or invalid could not be settled at all: the approval threw, and
with it went the refund, the stock and the return rows (NSUPDL5176, Mobile
Hub, GSTIN 08DCUPD7948K1ZP). Where the invoice itself had never been filed
the return was instead refunded with no credit note at all, leaving the
refund undocumented.
Raising the note automatically is not the answer - it is Finance's call,
and the GST on a cancelled-GSTIN sale is not recoverable, so paying the
full value back loses it. Both flows therefore record the refusal and stop.
GstProService: getIrnFailureReason (NIC's refusal for an invoice),
isInvalidBuyerGstin (that refusal is about the buyer's registration -
deliberately NOT a state-code mismatch, which is a data error to fix and
retry, nor anything naming our own seller GSTIN) and briefIrnReason (one
plain-ASCII line of 128 chars; credit_note is latin1 and sql_mode is empty,
so anything else would store as '?').
PurchaseReturnServiceImpl: both the invoice-return and the debit-note
refund detect that refusal, record it through ReturnIrnFailureRecorder and
throw ReturnIrnGstinFailureException. The recorder commits in REQUIRES_NEW
because the approval it records is about to roll back, and it only inserts -
return_irn_failure carries no foreign key on purpose, since an FK check
would take a shared lock on the very PurchaseReturnOrder row the dying
transaction may still hold.
Finance may then settle the return as B2C for the rest of that calendar day:
refundAsB2c credits the value NET of GST in whole rupees (B2cRefundQuote,
HALF_UP per line, so the line table and the total always agree), restores
the stock and issues a local credit note for exactly that amount with no tax
on its lines, no IRN and NIC's reason recorded. The amount is fixed - the
caller must pass back the quoted figure - and a remark and explicit consent
are required. On a later day the option only reopens after a fresh approval
attempt is refused again, so NIC is always re-checked first.
Mails: every refusal notifies Accounts L2 and above; the B2C settlement
notifies them and the partner's Warehouse L1/L2, both tabulated.
The partner account statement is unaffected: it skips RETURNS notes and
credits returns from returnorderinfo, so the new notes cannot double-credit.
DEPLOY ORDER: apply sql/add_return_b2c_refund_20260921.sql BEFORE any war
built from this dao. CreditNote now maps irn_skip_reason and b2c, so every
credit-note read in web, fofo and cron fails until the columns exist.
Untested beyond compilation. |
|
| 37785 |
11 d 3 h |
amit |
/trunk/profitmandi-dao/src/main/resources/sql/ |
feat(cs): demo partner access menu for Sales L6 and above
menu_category rows for Sales (category 4) ordinals 5-9 (L6..Final) on the
Demo Partner Access menu. INSERT IGNORE on the PK (latin1 column rejects a
utf8-literal NOT EXISTS compare). Applied on hadb1 2026-09-25. |
|
| 37779 |
11 d 6 h |
amit |
/trunk/profitmandi-dao/src/main/ |
feat(cs): demo partner access for sales team
New cs.demo_partner_access binding (sales auth user -> partner) used only to
open a partner's dashboard/app for demos. Read only at partner-view entry
points; CsService position mappings are untouched, so performance, targets,
reports and cron mails are unaffected.
- DemoPartnerAccess entity + repository
- DemoPartnerAccessService: grant (sales-only, active store, skips duplicate
and position-mapped), soft revoke, isAllowed (position OR demo),
demo emails for app lists, canManage (mirrors sidebar menu rule)
- AdminUser.ALL_MENU_EMAILS extracted so canManage and the sidebar share it
- migration_demo_partner_access.sql: table + "Demo Partner Access" menu
under Admin Control (NOT yet applied on hadb1) |
|
| 37778 |
11 d 21 h |
amit |
/trunk/profitmandi-dao/src/main/ |
fix(catalog): delist cron retires only models it darkened in the same run
Step 4 scanned every mobile model with no active listing and moved it to OTHER,
so new launches (categorised before listing) and hand-paused models were retired
the next morning, e.g. Nothing Phone 4 1026596-603 on 2026-09-15. Scope it to
models whose listing this run delisted (_delist_audit run_date = today).
Add fix_dark_model_retire_20260924.sql to restore the 16 models still stuck on OTHER. |
|
| 37776 |
11 d 23 h |
amit |
/trunk/profitmandi-dao/src/main/ |
returns: reverse a refunded debit-note return whose goods never reached the warehouse
Once a return was refunded nothing could undo it - rejectReturn refuses an already
refunded one - so a return booked and paid for goods that never arrived left phantom
warehouse stock, the unit missing from partner stock, the order marked returned, a
wallet credit and a filed credit note, with no way back.
ReturnReversalService.reverse(imei, reason, by, dryRun) undoes exactly one unit:
the warehouse return scan (row deleted so a genuine return later is not rejected as a
duplicate) and the stock it added, partner stock with its schemes/price drop/offers,
the order's return quantity and status, the wallet refund as a REVERSAL entry, and the
return item / debit note. Guards refuse anything that has moved since the refund, and a
dry run reports the plan without writing.
The credit note follows movement and the NIC clock: inside the 24h window, and only
when the note credits nothing but this unit, its IRN is cancelled and the note marked
cancelled; past it a DBN with its own IRN is issued against the note
(CreditNoteService.issueReturnReversalDebitNote, stored as CN_CANCELLATION with no
margin month so the statement does not show it twice - the wallet REVERSAL is the
statement line); a note never filed at NIC is voided locally. Every local write happens
first and NIC last, so a refusal rolls the whole reversal back.
fofo.return_reversal (return_reversal_20260921.sql) records each reversal and, unique
per return item, makes it once-only. Also adds cancelled=0 to the RETURNS_CN branches of
the account statement queries, without which a cancelled note keeps crediting the
statement - no current effect, no RETURNS note is cancelled today. |
|
| 37769 |
12 d 1 h |
amit |
/trunk/profitmandi-dao/src/ |
fix(movement): receive a movement invoice only against its own PO, stop auto-closing movements, cancel reduces the PO line
- GRN: movement invoice resolves invoice -> orders -> transaction -> its own movement PO (or a receive-only PO
from the billing warehouse); supplier/warehouse keyed at entry must match; scanned IMEIs must be the billed ones
- Cancelling an unbilled movement order takes its qty off the PO line; PO CLOSED/PRECLOSED once nothing is open
- reopenedAt removed (movements no longer auto-close); PO records createdBy
- SupplierStateResolver: a supplier's state is derived from its GSTIN on save
- sql: add_po_created_by (applied), drop_po_reopened_at (run AFTER fofo/cron/web deploy) |
|
| 37764 |
12 d 2 h |
amit |
/trunk/profitmandi-dao/src/main/ |
feat(store-closure): require reason, remark and approval mail to close a store; mail closure report
- fofo.store_closure audit table (reason, remark, approval document, closed_by); migration applied on hadb1
- StoreClosureService: validates and closes, queues closure report with approval attached to the
partner's Sales L2, top 3 Sales levels, top Accounts level and the closer
- StoreAccess: single home for store close/deactivate/activate/extend-billing access lists;
neeraj.gupta can close, mohit.gulati removed from extend billing |
|
| 37724 |
18 d 2 h |
amit |
/trunk/profitmandi-dao/src/main/resources/sql/ |
chore(hot-deals): restore 5 catalogs to OEM brand, add POCO M7 Plus 5G (4GB 128GB)_s to Hot Deal
Applied on hadb1 2026-09-18. Removed 1025252/1025253 (Samsung A06) and
1026085/1026403/1026408 (Refurbished iPhones) via _hot_deal_brand_freeze;
added catalog 1026568 (item 40832) with freeze + scope rows. |
|
| 37713 |
18 d 15 h |
amit |
/trunk/profitmandi-dao/src/main/ |
fix(warehouse): record the order's own vendor when a receipt line carries no origin
- r37694 took the origin only from lineitem.origin_vendor_id, which exists on orders raised from that revision onward, so goods arriving on older open orders landed with a cost but no vendor - 282 rows on the first day
- A purchase from an outside vendor now falls back to that supplier; internal movements are unchanged and an unknowable origin still stays NULL
- sql/backfill_receipt_origin_20260918.sql repairs the 282 already received (run on prod 2026-09-18; bak warehouse._bak_receipt_origin_20260918); in-stock unknown 895 -> 648 |
|
| 37708 |
18 d 15 h |
amit |
/trunk/profitmandi-dao/src/main/resources/sql/ |
Migration: state the algorithm and lock mode, and why it runs before the release
hadb1 is MySQL 5.7, so adding a column rebuilds the table rather than only touching metadata.
At ~53k rows that is seconds and InnoDB permits concurrent reads and writes throughout, but
ALGORITHM/LOCK are now stated explicitly so it fails loudly rather than quietly taking a table
lock if that ever stops holding. |
|
| 37704 |
18 d 15 h |
amit |
/trunk/ |
Reopen a movement PO whose stock arrived late, and stop stranding GRN price corrections
Internal movements auto-close after four days, which fits 99.6% of them - 5,491 of 5,515 receipts
land inside the window. The remainder leave the PO closed with the stock still in transit and
nowhere to receive it: 998 internal POs closed during 2026 still holding 21,996 unreceived units.
A closed movement PO can now be reopened from the purchase order list. Reopening stamps
reopenedAt, and auto-close measures from WarehousePurchaseOrder.getOpenSince() - reopenedAt when
set, the PO date otherwise - so a reopened PO gets the same fresh window a new one gets instead of
being closed straight back on the next sweep. Only movements between our own warehouses: an
external vendor PO that has closed is settled with that vendor, not reopened unilaterally.
Separately, a GRN price correction now checks that the PO it just raised is one the invoice can
actually be received against. Matching reads POs that are open and approved for the same supplier
and warehouse dated on or before the invoice; it never looks at the PO being corrected, so what
matters is that the new PO is receivable. 54 were not - backdated into INIT by the old approval
gate, hence outside the match - and each stranded silently: original line discarded, GRN completed
without it, the correction left holding a reservation for stock that had already arrived.
isOpen() names the open set - INIT, READY, PARTIALLY_FULFILLED - that the movement and commitment
queries already read.
Migration sql/add_po_reopened_at_20260918.sql adds reopenedAt, nullable and additive. It must run
before this ships: the entity maps the column. |
|
| 37694 |
19 d 1 h |
amit |
/trunk/profitmandi-dao/src/main/ |
feat(warehouse): record where stock came from and what it cost; price internal movements at that cost
- inventoryItem.origin_vendor_id / receipt_unit_cost and lineitem.origin_vendor_id (sql/add_inventory_cost_layer_20260917.sql, applied on prod 2026-09-17 in 75s): GRN copies both from the PO line it arrived on, so origin survives any number of hops and non-serialised stock keeps it too. Unknowable origin stays NULL - never a guess, never an internal supplier
- A movement now moves stock at the cost it was received at, not the origin vendor's current catalog TP. Layers are grouped by cost; a quantity spanning two costs is refused with both numbers, since a PO line carries one price
- Stock already promised on open outbound movement orders is no longer offered again (176 item/warehouse pairs on prod are fully promised today)
- Stock on open inbound POs is offered for planning at that order's price and reported as 'on the way in', but cannot be dispatched until received; the PO item picker lists those items too
- Backfill: 4,010 of 4,646 in-stock rows on prod got origin and cost; 636 stay unknown and are priced from the latest approved external catalog price as before |
|