Subversion Repositories SmartDukaan

Rev

Show changed files | Directory listing | RSS feed

Filtering Options

Rev Age Author Path Log message Diff
37858 1 d 17 h ranu /trunk/ remove tarun sir email from some storetimline tat email  
37845 2 d 16 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)  
37830 6 d 9 h amit /trunk/profitmandi-dao/src/ feat(movement): one PO per source vendor warehouse and cost - layers carry their GOOD/OURS vendor warehouse, orders are raised pinned to it, holds of pinned orders come off their own warehouse, billing takes units at the order's price first  
37822 6 d 14 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).
 
37817 7 d 18 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/ fix(movement): shelf stock before arriving, one PO per cost, close PO on invoice cancel

- Internal movement fills from shelf stock before stock still arriving (a pending PO dated at
midnight ranked ahead of same-day receipts, pricing movements from units that never shipped).
- A quantity spanning stock at different costs is raised as one PO per cost at once instead of
being refused (first PO takes every item's oldest cost, the next the following cost).
- Refuse a movement from a warehouse to itself.
- Cancelling a movement's invoice/DC takes the order's qty off its PO and pre-closes it when
nothing is left open, as refunds already did.
 
37792 9 d 19 h amit /trunk/profitmandi-dao/src/ feat(cart): carry bag at Rs 1 per smartphone over Rs 12,000, extras at listing price

One carry bag (item 32046) is priced at Rs 1 for each smartphone (category
10006) in the cart selling above Rs 12,000; bags beyond that count are priced
at the carry bag's listing price, and Bronze partners pay listing for all.
Closes the leak where bag-only carts of hundreds of bags at Rs 1 were placed
from the old app. CarryBagQuote (via CartService.getCarryBagQuote) is the
single rule; the cart carries it as one line at a blended price.

- CartService/CartServiceImpl, CartResponse, OpenCartValidationResult: expose
the quote on the cart.
- OrderLineAllocator: an item may now have more than one cart line (the flat
and listing-priced parts); its units fill the lines in order.
- TransactionServiceImpl: item quantities merge instead of failing on a
duplicate item id.
- PurchaseServiceImpl: partner GRN creates one stock record per billed price;
the GRN screen shows the blended unit price; dead
createScannedNonSerializedItem removed.
- PurchaseReturnServiceImpl: debit-note PDF picks the order billed at the
returned stock's price; refundOrder spreads a non-serialized return over the
item's orders with room left (same price first) instead of loading it all
on the first order.

Tests: CarryBagQuoteTest 6/6, OrderLineAllocatorTest 13/13.
Deploy with profitmandi-web (same change) and the partner apps.
 
37775 13 d 15 h ranu /trunk/ aging po approval process  
37761 13 d 22 h ranu /trunk/ aging po approval process  
37759 14 d 15 h vikas /trunk/ LMS Call for App  
37758 14 d 15 h ranu /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/model/ aging po approval process  
37757 14 d 15 h ranu /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/model/ aging po approval process  
37750 14 d 16 h ranu /trunk/ aging sku purchasing need to approval of niranjan kala sir  
37731 16 d 22 h amit /trunk/profitmandi-dao/src/main/ Remove dead third-party integrations: dao

Nothing removed here had a live caller. No tables are touched.

- Toffee Insurance client + models, and the tofee.* keys in shared-*.properties
- Bharti Assist (BAG) service and its certificate/brand models. BagPlanModel
and PlanVariant stay in bharti/model: OneAssist and ICICI Lombard use them.
- Private, uncalled Toffee/BAG request builders in InsuranceServiceImpl
- Wiseapp insurance client and ZestResponseModel
- SmartPing client. CallDetailModel and PushCallLogModel move to
kommuno/model because the Knowlarity webhooks still parse them.
- SpiceMoney SSO (2-partner pilot), Thriwe stub + DTOs, DTDC/Shipsy (demo
URLs only), Blue Dart SOAP stubs, legacy Pine Labs v1 client
- FundFina pre-approval entity/repository, HyperTrack key entity/repository,
affiliate Click entity/repository
- Speqtra SMS constant; aramex.tracking.url keys
- PAYU PAY entry in payment-options.json

Gateway.MANDII and Gateway.FUNDFINA stay: historical fofo.payment rows.
 
37729 16 d 22 h amit /trunk/ lead: one workable lead per mobile, with a 6-month supersede and an L2+ override

There was no choke point for lead creation. Ten sites did `new Lead()` across four
modules -- three in web's LeadController, two in V2FofoLeadController, three in fofo's
LeadController, one in TrialServiceImpl and one in the cron LeadSyncRunner -- and only
ONE of them (fofo /createLead) checked for an existing lead at all. Result on live data:
5,137 mobiles carrying duplicate leads over 12,156 rows, worst case 29 on one number,
and two agents unknowingly working the same shop.

THE RULE, in new LeadCreationService, which all ten now route through:

no active lead on the number -> create
active, last activity >= 6 months -> retire the old one, create the new one, SILENTLY
active, last activity < 6 months -> BLOCK; only an L2+ user may override

Active = status in (pending, followUp) AND the assignee is still an active auth_user.
Last activity = GREATEST(lead.updated/created, MAX(lead_activity.created)).

The stale branch is deliberately quiet. A shop enquiring again after six months is a
handover, not a clash, and mailing on it would train the desk to ignore the alert -- so
only a genuine collision notifies. Live split: 195 stale against 1,241 fresh, and roughly
three blocks a month.

WHY "ACTIVE" ALSO MEANS A LIVE OWNER

331 open leads are assigned to 11 DEACTIVATED accounts (157 to sm@smartdukaan.com alone,
whose newest lead is from 2022). Counting them as active would block fresh enquiries
behind an account nobody can log in to and therefore nobody can close. Requiring a live
owner defuses all 331 without retiring a single row. Retirement here is only ever
REACTIVE -- triggered by a new entry on the same number. Nothing runs on a schedule.

ASSUMPTION worth flagging: a superseded lead becomes status=notInterested (stage DROPPED)
with closure_timestamp and reason 'Superseded after 6 months inactivity', rather than a
new `expired` status. "Closed" is an explicit allow-list in the UI --
Arrays.asList(notInterested, finalized) at V2FofoLeadController:150 and fofo
LeadController:313 -- and there are ~107 references to specific LeadStatus values, so a
new enum value would make these leads vanish from BOTH the open and closed screens.
Stage DROPPED keeps the nuance (the shop never said no) and still maps to notInterested
via LeadStage.toLegacyStatus().

OVERRIDE is L2+ in ANY team, not Call Center only: Sales L1 owns 1,063 of the 1,776 open
leads, so a Call-Center-only gate would funnel every team's collisions through three
people. The MAIL still goes to Call Center L2+, resolved from cs.position at send time
rather than hardcoded. An override is a TAKEOVER -- it closes the existing lead -- because
a second live lead is the exact thing the rule exists to prevent.

UNATTENDED CALLERS (cron sync, CSV upload, trial registration, AI intake) have nobody to
offer an override to, so they use createUnattended: skip the colliding row and mail the
desk rather than throwing. CSV reports imported/duplicateSkipped/duplicateMobiles back to
the operator instead of failing the whole file over one number.

ALSO FIXES selectByMobileNumber, which called getSingleResult and therefore threw
NonUniqueResultException on any mobile with more than one lead -- GlitchTip #99 and #1359,
both still firing. It now prefers the open lead, then the most recently touched.

NOT INCLUDED, deliberately: no DB unique constraint. 10 mobiles already carry more than
one open lead and would have to be resolved by hand first, which conflicts with the
no-auto-retirement rule. The service enforces the invariant going forward.

NEEDS A DBA STEP: user.lead.mobile is unindexed on 37,580 rows, so this check is a full
scan on every create. Index DDL is in the accompanying note; it has NOT been applied.
 
37722 19 d 17 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/ Movement PO availability: list stock arriving into the sending warehouse, per PO

The popup showed in stock, promised and can-move, but not stock on its way in once it was
fully promised - so UP to Noida read 'in stock 0, promised 4, can move 0' with the 4 units
arriving on PO 54031 nowhere, and looked wrong. It now reads as a sum:
in stock + arriving - promised = can move.

- selectOpenInboundPurchases: open POs into the warehouse, one row per PO, same filter as the
pending-stock layer so the rows add up to the arriving total
- InternalMovementAvailabilityModel: arrivingTotal (before holds) + arriving breakup;
inbound (after holds) unchanged, still used by the refusal message
- describeAvailability (single + batch) carries both; batch doc: quantities are no longer
checked at bulk upload, the screen flags them and order creation enforces them
 
37700 20 d 10 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/ Internal movement: order against expected stock, and stop reserving lines that can no longer ship

Two rules changed, both about stock that was being treated as unavailable when it is not.

Stock still to arrive now counts toward what a movement order can be raised for. A movement is
planned against what the warehouse will hold, so the order is raised now and dispatched once the
stock lands; previously a warehouse holding only inbound stock refused outright, even though the
receipt was already on an open order.

A purchase order line stops reserving stock once its own order moves past being submitted for
processing. Billed or shipped means the units have already come off the shelf; cancelled means
the line can never dispatch them. Holding either back a second time hid stock that was genuinely
free - on prod, 660 of 675 reserved units were on lines whose orders had already moved on. A line
whose order has not been raised at all is still a live promise and keeps its hold, and a line is
held only for the quantity still sitting in an in-process order, so a partly-shipped order
releases the rest.

priceFor and describeAvailability continue to read stock through one shared examine(), so what is
shown while choosing a quantity stays exactly what the order is created against.
 
37696 20 d 12 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/ Internal movement: report what a warehouse can actually send, not just refuse on submit

The quantity a movement order can take was only discovered by submitting the order and being
refused. The stock reading that decides it already computed everything needed to say so in
advance - what is on the shelf, what older orders have promised, what is still arriving - and
then discarded it.

selectOpenOutboundCommitments returns those promises one row per order, with the PO number,
PO date and destination, instead of a single summed quantity. applyOutboundCommitments now
aggregates those rows and behaves exactly as before, so no extra query is run.

describeAvailability answers with the price plus that working. It shares one stock reading with
priceFor via examine(), so the cap shown while choosing a quantity and the cap enforced on
submit cannot drift apart. Unlike pricing it reports rather than refuses: stock that is entirely
promised comes back as zero movable with the orders holding it named, which is what lets someone
chase an abandoned order rather than only see a smaller number.
 
37694 20 d 16 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
 
37679 20 d 18 h amit /trunk/profitmandi-dao/src/main/ feat(warehouse): carry the PO creator through purchase order creation for backdated auto-approval

Backdated POs are to be auto-approved in the name of whoever raised them, but the service had no way
to know who that was: the model carried no user and buyerId is the seller, not a person.

- CreateWarehousePurchaseOrderModel carries createdBy
- resolvedMismatchRequest takes resolvedBy, so the correction PO raised on a GRN price mismatch -
dated to the supplier invoice and therefore backdated by nature - is credited to whoever resolved it
- staged backfill SQL for the three backdated POs still stuck in INIT inside POScheduler's
auto-close window; data only, no schema change, not yet run

The release-to-READY change in PurchaseOrderServiceImpl follows separately.
 
37656 21 d 17 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/ Point dao at the relocated KYC and SD Credit types (r37655)

Imports follow services.mandii -> services.kyc / services.sdcredit, and
RecordingService takes RawHttpResponse in place of MandiiResponse.

Gateway.MANDII is kept, with a comment saying why: it is persisted as a
string on FofoPayment.gateway and CreditAccount.gateway, and 740 historical
fofo.payment rows still carry it - removing the constant would make
Hibernate throw when reading them.
 

Show All