| Rev |
Age |
Author |
Path |
Log message |
Diff |
| 37626 |
1 h 26 m |
amit |
/trunk/profitmandi-fofo/src/test/java/com/spice/profitmandi/service/warehouse/ |
test(billing): assert internal transfer and billing resolve the same external origin vendor |
|
| 37625 |
1 h 26 m |
amit |
/trunk/profitmandi-dao/src/main/ |
fix(internal-movement): trace serial origin to the latest external purchase, matching billing
- resolveInternalMovementPrices orders the serial's external purchases newest first (was oldest), so transfers and billing name the same original vendor; no in-stock unit on prod changes (0 of 3,734)
- Restore resolveInternalMovementPrices javadoc onto its own method
- Add drop_internal_vendor_catalog_pricing_20260914.sql, as run on prod 2026-09-14 17:33 (15 New Spice internal suppliers; backups _bak_vcp/_bak_vcpl_internal_20260914) |
|
| 37624 |
3 h 13 m |
amit |
/trunk/profitmandi-web/src/main/java/com/spice/profitmandi/web/v2/controller/fofo/ |
feat(price-drop): auto-approve V2 price drop DP/MOP into external vendor catalog pricing |
|
| 37623 |
3 h 13 m |
amit |
/trunk/profitmandi-fofo/src/ |
feat(price-drop): auto-approve price drop DP/MOP into external vendor catalog pricing
- PriceDropController calls VendorCatalogPricingService.applyPriceDrop with the affected date and logged-in user
- BillingPricingServiceTest: local-DB integration tests (rolled back) for origin-based billing prices, catalog fallback and re-billed orders |
|
| 37622 |
3 h 13 m |
amit |
/trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/ |
feat(billing): price warehouse billing from the external supplier of the billed stock; auto-approve price drop DP/MOP into vendor catalog pricing
- BillingPricingService resolves TP/NLC per order from vendor_catalog_pricing of the most recent external supplier of the units scanned out (serial trace, else own external PO); units reversed by SALE_RET are ignored
- Falls back to the latest approved external catalog price when no supplier can be traced; vendorId stays the warehouse vendor
- addBillingDetailsForGrouppedOrders no longer reads vendoritempricing (removes NPE when the row is missing); order.vendorId set to the origin supplier
- VendorCatalogPricingService.applyPriceDrop writes approved pricing logs for external vendors with the price drop DP/MOP, keeping each vendor's TP on the effective date |
|
| 37621 |
3 h 14 m |
amit |
/trunk/profitmandi-web/src/main/java/com/spice/profitmandi/web/v2/controller/fofo/ |
V2 /entity honours activeOnly, defaulting to false
Same defect as the fofo endpoint fixed in profitmandi-fofo r37620: the flag was
accepted and ignored. Defaulting to false keeps callers that omit it unchanged. |
|
| 37620 |
3 h 14 m |
amit |
/trunk/profitmandi-fofo/src/main/ |
Offer creation suggests only active models unless asked for all
/entity accepted activeOnly but always searched with false, so the offer
screen's model picker listed every catalog of the brand, delisted ones included.
It now honours the flag. The default becomes false so the pages that never pass
it keep their current results; the notification product search, which already
asked for active-only, now gets it.
Each item-criteria block gets an Include inactive checkbox, off by default.
Ticking Exclude ticks it too: a brand-level offer still pays on delisted models
partners hold stock of, and they cannot be excluded if they cannot be picked.
Inactive models are labelled, and reloading the list keeps picks still present.
The reload flag is now per block; the old global one let a brand change in one
block be consumed by opening another, leaving the first with a stale list.
Brand names are URL-encoded so a brand like Ai+ is not sent as 'Ai '.
Requires profitmandi-common r37619 for the inactive label. |
|
| 37619 |
3 h 15 m |
amit |
/trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/solr/ |
Return active_b in the unlimited content search
The offer screen's model picker needs to mark delisted models as inactive when
it is asked to include them. The other unlimited-search callers read only
catalogId_i and title_s, so the extra field changes nothing for them. |
|
| 37618 |
5 h 11 m |
ranu |
/trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/transaction/ |
logger added |
|
| 37617 |
14 h 46 m |
amit |
/trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/ |
Undo an internal GRN so its invoice can be received again
A receipt taken before the serials were split left stock that cannot be corrected in place: a
serialised unit is held one to a row, so an invoice that should have created several rows created one,
and the rows that are missing cannot be added without also unpicking the running figures the receipt
moved. Reversing the receipt and taking it again through the same path is the only way to arrive at
what the invoice actually says.
The reversal removes what receiving created - the scans, the inventory units, the invoice items, the
purchase and the supplier invoice - and gives back the two running figures it moved: the warehouse
availability, and the quantity taken off the purchase order line. Both are worked out from the rows
being deleted rather than recomputed, so whatever the receipt added is exactly what comes off. A
purchase order the receipt closed is opened again. Availability is kept only as a running count with
nothing to rebuild it from, which is why it is adjusted by what is known to have been added rather
than by anything inferred.
An invoice whose stock has moved since is refused, not worked around. A unit that has been scanned out
or partly consumed is no longer the receipt's to remove, and deleting around it would leave the
warehouse holding stock that no record explains. Each invoice is reversed on its own so one refusal
leaves the rest untouched, and a dry run reports everything it would delete and every figure it would
change without writing.
Invoices are named explicitly rather than selected by date, so a reversal can only ever touch what it
was given. |
|
| 37616 |
14 h 55 m |
amit |
/trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/migrations/ |
Split a line item's serials into one inventory unit each
A serialised line item carries one serial per unit in serial_number, comma separated. The internal
GRN passed that whole field across as a single serial, so a line of four units became one unit whose
serial was the four serials joined together - a string no scan can ever match. Where the joined
string ran past the 128 characters the inventory column allows, the receipt failed outright; where it
fitted, it was accepted and the stock was quietly understated.
The serials are now split out and each unit is received on its own, which is what grnPoModels expects
- it creates one inventory row per serial.
A serial count that does not match the line's quantity now skips the invoice. Receiving fewer units
than were billed is exactly the failure this had, and it is not something to infer a best guess from:
the invoice is left for someone to look at instead. |
|
| 37615 |
15 h 27 m |
amit |
/trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/migrations/ |
Receive internal transfers the way the portal does, not via the Excel upload
The live run failed on every invoice with "Column 'status' cannot be null". The Excel upload path it
was calling, addPORowModels, persists a supplier invoice without ever setting a status, and the
column does not allow one to be absent, so that route cannot complete a receipt at all.
Rather than change a path the portal shares, this follows what the Receive Invoice screens actually
do: record the supplier invoice, record its items through InvoiceService.createInvoiceItem, then hand
it to PurchaseOrderService.grnPoModels, the call behind the Create GRN button. grnPoModels sets the
invoice to received itself, so the status is never left for the caller to remember.
Recording the invoice items matters beyond the receipt: warehouse.invoice_item is what the buying
reports join against, and the Excel path never wrote those rows either.
An invoice is carried as one entry per item rather than one per order, since that is the shape
grnPoModels expects - every serial of an item arrives together and a non serialised item arrives as a
single quantity. There is no supplier document to attach, which is ordinary here; most existing
warehouse invoices carry none.
Resolving the purchase order now uses the mapping recorded when the internal PO was raised - the
transaction it created, held on the purchase order - instead of walking every order to find it.
An invoice whose orders do not share that one transaction is skipped rather than guessed at. |
|
| 37614 |
15 h 37 m |
amit |
/trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/migrations/ |
Run each internal GRN in a transaction that is actually applied
The internal GRN one-off failed immediately with "no transaction is in progress", thrown while
flushing the Hibernate session at commit.
The per invoice method was annotated to start its own transaction, but it sat in the same bean as
the loop that called it. Spring applies @Transactional through a proxy, and a call from one method
of a bean to another never leaves the object, so the annotation was inert - the receiving ran with
no transaction at all while the driver had suspended the surrounding one. The repositories still
bound a session to the thread, and the flush at commit then found nothing to flush into.
The receiving moves to its own bean, so the driver now reaches it through the proxy and the
transaction is real. The driver keeps no transaction of its own, which is what lets one invoice
fail without disturbing those already received.
No change to what is received or to the conditions under which an invoice is skipped. |
|
| 37613 |
17 h 3 m |
amit |
/trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/ |
Receive internal transfer stock that was billed but never GRNed
Stock moved between our own warehouses is dispatched against an internal purchase order and
billed, but the destination warehouse still has to receive it by hand through the portal. Where
that never happened the units exist on an invoice and nowhere in inventory, and there is no way
to clear a backlog of them short of keying each invoice in again.
This adds a cron one-off that receives them. Per invoice it builds the rows the Excel GRN upload
would have carried and hands them to PurchaseOrderService.addPORowModels, which creates the
supplier invoice, the purchase and the inventory items in a single call, so none of receiving is
reimplemented here - the portal and this take the same path and can only ever agree.
It is deliberately narrow about what it will touch. Only INTERNAL buyers, because nothing should
be able to receive a partner's goods on their behalf. Only invoices with no supplier invoice
already recorded, so a repeat run skips what it has already done rather than receiving twice.
Only invoices resolving to a single purchase order, since addPORowModels resolves one order for
the whole map it is given and would otherwise attribute an invoice to the wrong one - for the
same reason each invoice is passed on its own. A serialized line whose serial number is missing
aborts its invoice instead of creating a unit no scan could ever match.
Each invoice commits or rolls back on its own, so one bad invoice cannot undo the ones already
received, and a dry run reports what it would receive without writing anything.
The receipt is recorded against the destination warehouse, which is what warehouse inventory is
keyed by. The buyer on the order is read only to confirm the store is internal; partner side
inventory is a separate receipt and is not touched. |
|
| 37612 |
1 d 17 h |
amit |
/trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/warehouse/ |
Value an internal PO's cart the same way the transaction validates it
Creating an internal PO failed with "Cart Value-Payment value mismatch" whenever a line
carried paise and the quantity was more than a handful. Two places were computing the same
cart total from the same stored float through two different decimal conversions, so they
disagreed on the value of prices binary cannot hold exactly.
createOrderInternally read WarehouseLineItem.unitPrice through String.valueOf, which
resolves a float via Float.toString and yields 10499.99, while createTransactionForWarehouse
re-values the persisted cart lines with BigDecimal.valueOf, which widens that same float to
double and yields 10499.990234375. The per-unit gap is around two ten-thousandths of a rupee;
quantity multiplies it, and it crosses the 0.001 tolerance the validator allows at a quantity
of five. Round prices are exact in binary and passed, which is why this looked intermittent
rather than total - across randomised carts the old arithmetic disagreed 98% of the time.
The total is no longer computed in its own loop. It is derived from the cart items that are
about to be written, using the conversion and the line-inclusion rule the validator applies,
so both sides are the same function over the same rows and cannot drift apart. That also
brings across two rules the PO side never had: quantities of zero or less, which
addItemsToCart drops and which therefore never reach the validated total, and the one paisa
carry bag, which the validator treats as a marker line rather than a billed one. Either of
those reaching an internal PO would have failed it outright, and a carry bag is only a
rounding error away from the paisa-per-unit pricing used for FOC stock.
The wallet top-up still truncates the total to whole rupees, and now lands within a rupee of
where it did before, well inside the twenty rupee buffer it already carried. Nothing outside
internal PO creation is touched: the validator, bulk orders and the refurb split keep their
existing numbers, and the wallet debit is driven by the order totals, not by this value. |
|
| 37611 |
1 d 18 h |
amit |
/trunk/profitmandi-fofo/src/test/java/com/spice/profitmandi/service/warehouse/ |
Pin the unrecorded-stock behaviour
Covers the shape behind the 2026-09-12 bulk-upload failures: an item with some stock
traceable to a vendor and some with no recorded origin must move in one order, at the
traceable stock's price. Two genuinely different vendors must still refuse.
Also pins that no refusal message quotes a zero price from either side - including when the
oldest stock is itself the unrecorded pile, which is how 'at 0.00 each' reached users.
Requires profitmandi-dao r37610. |
|
| 37610 |
1 d 18 h |
amit |
/trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/warehouse/ |
Carry stock with no recorded origin along instead of refusing the movement
A movement was refused whenever an item had some stock traceable to a vendor and some whose
origin was never recorded, telling the user to raise a second purchase order for the rest.
Unrecorded stock has no vendor of its own, so that second order carried no more information
than the first - and the prices were identical anyway: boAt 34336 at 85 both ways, Samsung
36343/36344 at 1099, Riversong 36589 at 524, 36590 at 657. Bulk uploads of 200 rows were
dying on the first such item, repeatedly.
Only a second ORIGINAL VENDOR stops an order now, which is the case the refusal was for: a
line item holds one price per item, so units genuinely bought from someone else cannot ride
along. Unrecorded units move with the oldest stock at its price. They are not attributed to
that vendor as a fact - nothing persists origin today, but if that is ever added they must
not be stamped from this.
The refusal also reported unrecorded stock as moving at 0.00. Such a bucket carries no price
until it is resolved from the catalog when units are allocated, so reading one off it early
reported perfectly good stock as worthless. Both sides of the message now resolve the price
the same way the allocation does.
Three tests added: a mixed traceable/unrecorded item moves in one order, two genuinely
different vendors still refuse, and no message quotes a zero price from either side. |
|
| 37609 |
2 d 1 h |
amit |
/trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/ |
Offer the stock a warehouse actually holds when ordering from an internal supplier
The purchase order item picker listed whatever the supplier had vendor catalog pricing rows
for. For an outside vendor that is right - the order is for stock nobody holds yet, so their
catalogue is the only sensible list. For one of our own warehouses it is not: a movement can
only send stock that is standing there, and the pricing rows were never a statement about
stock. Vendor 275 was offering 6,845 items while Noida held 79; Delhi 6,637 against 330.
It also made the picker depend on data the movement no longer needs. Pricing for internal
suppliers is derived from the stock itself since r37603, so those rows are inert - but
clearing them emptied the picker completely, because selectVendorItems joins
VendorCatalogPricing to Item and an internal supplier then matched nothing.
Internal suppliers now list distinct items with currentQuantity > 0 in their mapped
warehouse. External suppliers are untouched and still list from vendor catalog pricing. |
|
| 37608 |
2 d 2 h |
amit |
/trunk/profitmandi-dao/src/main/resources/sql/ |
Select a default schema before the multi-table deletes
MySQL's 'DELETE alias FROM' form needs a default database even when every table is fully
qualified, so the deletes aborted with 'No database selected' while the backups above them
had already been written. Added USE inventory so the script runs end to end regardless of
how the client is invoked.
Run on hadb1 2026-09-12: 12,372 + 17,241 + 33,252 internal-supplier rows removed, external
pricing untouched at 40,670 / 45,049 / 68,749. |
|
| 37607 |
2 d 2 h |
amit |
/trunk/profitmandi-dao/src/main/resources/sql/ |
SQL to remove vendor pricing held against internal suppliers
Nothing reads these since r37603-05: an internal movement is priced from the stock being
moved, resolved to the original external vendor, and receiving no longer validates price for
those movements. A stale row would silently win over the derived price if any lookup were
reintroduced, so they are worse than inert.
Backs every row up to a _bak_ table and deletes against those frozen backups rather than
re-reading supplier.internal, so what is removed is exactly what is preserved. Run only
after the fofo WAR carrying the warehouse-grn-request-items.vm guard (r37606) is live. |
|