Subversion Repositories SmartDukaan

Rev

Show changed files | Directory listing | RSS feed

Filtering Options

Rev Age Author Path Log message Diff
37842 2 d 8 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/ Fixed mail sender everywhere  
37841 2 d 8 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/ Fixed mail sender everywhere  
37816 7 d 8 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/ fix(einvoice): save the e-way bill whenever NIC returns EwbNo, not only with the EWBPPD note

GstProService saved EWB details only when InfoDtls held EWBPPD. NIC sends that
note (its pin-to-pin distance) only when we pass Distance=0, and the DUPIRN
get-by-IRN response carries no InfoDtls at all. So every partner with a
warehouse_partner_distance_mapping row, and every IRN recovered after a
timeout, lost its EWB although NIC had filed it (13 + 2 invoices in 30 days,
backfilled 2026-09-30), and a false "EWB Not Generated" mail was sent.

- Key on EwbNo; distance from EWBPPD, else the distance we sent, else none.
- Invoice PDF prints the "[distance]" suffix only when a distance is known
(was "[null]").
 
37809 8 d 9 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/ fix(einvoice): keep waiting for a fresh NIC auth token instead of giving up after one wait

getUsableAuthToken waited out an expiring token once and failed if NIC still
handed back one with under 60s of life. NSNOI14473 (2026-09-10) waited out a
token expiring 17:12:53, got one expiring 17:13:00 (6s left) and was parked at
irn_generated=0; the next invoice 5s later filed fine. Now waits up to 3 times,
each bounded by the remaining life of the token in hand.
 
37790 9 d 10 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.
 
37739 16 d 11 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/ GstProService: drop imports only the removed Perfios block used

HttpHostConnectException and JSONObject had no other use after r37736.
 
37736 16 d 12 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/ Remove dead Perfios GST-return OTP block from GstProService

Commented-out since it was added; the Perfios UAT URL and auth key were the
only trace of that integration. Part of the dead-integrations cleanup
(r37730-37735).
 
37647 22 d 3 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/ Invoice/DN return: file credit note at NIC only when the invoice IRN was filed

- GstProService.hasFiledIrn: invoice has a real IRN with ack date (DCs excluded)
- applyInvoiceReturnViaCreditNote: filed IRN -> CN + NIC CRN as before;
not filed + INTERNAL buyer -> CN issued locally, NIC skipped;
not filed + other buyer -> refund without CN, CN sequence untouched
- DN refund path: not filed + INTERNAL buyer -> local CN, NIC skipped
Fixes approval failing with 'Recipient GSTIN state code does not match' on
internal invoices whose IRN was never generated (NSPRJ41943, NSPRJ41950).
 
37538 32 d 19 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/ gstpro: a shipped delivery challan is a return, not a cancellation

Movement becomes the single discriminator for both document types. Nothing has
left the warehouse, so nothing happened and the document can be withdrawn; once
the goods have shipped the document records a movement that really occurred, and
the only honest reversal is an opposing document - a credit note through finance.

The clock is the only thing that differs. A tax invoice must also be inside its
24h IRN window, because past that NIC will not cancel the IRN. A delivery challan
has no IRN and no acknowledgement - every DC row carries a placeholder irn and a
null ack_date - so no clock applies to it: an unshipped challan stays withdrawable
whatever its age, and its e-way bill is cancelled on that path since nothing moved
under it.

Previously a DC short-circuited to cancellable regardless of shipping, which would
have voided challans whose goods were already in transit.
 
37532 34 d 6 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/ gstpro: do not cancel a DC e-way bill once the consignment has shipped

Three cases on cancelling a delivery-challan invoice:
- no EWB recorded: nothing to cancel at NIC
- already shipped: leave the EWB intact and void the challan locally. The goods
moved under that bill, so it is the record of a journey that happened; cancelling
would strip cover from it, and NIC refuses the cancel anyway once a bill has been
verified in transit. The return leg is a fresh movement needing its own EWB.
- never dispatched: cancel the EWB, since nothing moved under it.

Also reuses the orders already fetched for the shipped check rather than selecting
by invoice number twice.
 
37529 34 d 6 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/ purchase return: auto-approve invoice cancellation where it is safe

GstProService.isCancellableWithoutApproval: a DC invoice cancels without approval;
anything else needs the order to be unshipped AND the IRN to still be cancellable.

PurchaseReturnServiceImpl marks the order INVOICE_CANCELLED with the refund actor,
timestamp and reason recorded, and sets each line item's returnQty to its full
quantity - a cancelled invoice returns everything on it.

Committed on behalf of the working copy; dao goes first so the method exists before
the fofo controller that calls it.
 
37456 39 d 17 h amit /trunk/ Block billing when NIC rejects the transporter GSTIN for e-way bills

NIC files the e-way bill alongside the IRN, so a deregistered transporter
GSTIN returns Status=1 with InfoDtls[InfCd=EWBERR] (3029 "GSTIN - ... is
not active"): the IRN is valid while ewb_no stays null. Nothing downstream
reads that as a failure, so invoices kept being issued for goods that
could not legally move.

Cache the rejected transporter GSTIN in Redis and refuse to bill through
it. The block is keyed on the GSTIN, since one GSTIN is shared by several
warehouse_provider rows, and it carries the day it was raised so it lapses
at midnight and each new day re-tests NIC once. Correcting the GSTIN in
the provider panel lifts it immediately.

Only errors that are the transporter's fault block billing - NIC's 3029,
or any message naming the GSTIN we sent as TransId. Every other EWBERR
behaves as before: the IRN is filed and the e-way bill is retried later.

Guard sits in addBillingDetailsForGrouppedOrders before the pessimistic
lock and before any mutation, mirroring LogisticsServiceImpl#getEwbDetails
(order's own warehouse; self-pickup and runner skipped, as they travel on
a vehicle number rather than a transporter id).
 
37452 39 d 17 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/ Fix e-invoice IRN failure when the NIC auth token is about to expire

NIC reissues the same auth token until it genuinely expires, so evicting the cache
inside the 5 minute leeway hands back the identical near-dead token. getAuthenticatedHeaders
checked the leeway once, before that re-fetch, and never inspected what came back.
NSUPHR1770 filed with a token that had 12 seconds of life left and the gateway answered
412/GSP752 'eInvoice AuthToken not found or expired'; the invoice filed a minute later,
past the expiry and so on a genuinely new token, succeeded.

getUsableAuthToken re-checks the refetched token and, when it is still inside a 60s
floor, waits out the remainder before minting again. The wait is bounded by that floor
and every caller on this path is a cron/async thread already running against a 60s NIC
timeout with no transaction held open.

Also make the gateway's own failures legible. NIC rejections arrive as
{Status, ErrorDetails}, but failures the ASP raises in front of NIC use
{status_cd, error{error_cd, message}}, which shares no field name with RespPl: Gson
produced an all-null object and the stored reason degraded to the literal
'RespPl{Status=0, Data=null, ErrorDetails=null, InfoDtls=null}', indistinguishable from
NIC rejecting the document. RespGSPErr already modelled that envelope but was a
non-static inner class and so could not be instantiated by Gson; made it static and
copy the code and message into ErrorDetails, which every caller already reads.
 
37421 42 d 8 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/ EWB: recover existing e-way bill on NIC 604 instead of storing a placeholder

DCNSUPDL948 generated EWB 451765074092 at NIC, then the transaction rolled back and
the number was lost. Every retry hit 604 and stored the literal EXISTING-LOOKUP-NEEDED
with no validity date, which routed the PDF down the transporter branch and NPE'd on a
self-pickup dispatch with no warehouse_provider row. ~640 retries in 55 minutes, and
because the failure surfaced as an Error it escaped catch(Exception) and blocked six
other documents behind it.

- GstProService: on 604, look the bill up via GetEwayBillsByDate + docNo match and
return it in GENEWAYBILL shape; stamp NIC's generation time instead of now().
Throw when it cannot be recovered rather than persist a placeholder.
- InvoiceService: saveInvoiceInNewTransaction no longer propagates. It commits
irn_generated=0 with the reason so a failed document stops churning; already-filed
invoices being re-rendered are left untouched.
- InvoiceService: cron loop catches Throwable so one bad document cannot skip the batch.
- InvoiceService: null-guard warehouse_provider; omit the transporter line instead of
failing the PDF.
- recordIrnFailure: transport failures now park at 0 for escalation rather than
requeueing for unbounded retry.
 
37371 48 d 11 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/ Fixed mail sender everywhere  
37370 48 d 12 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/ Fixed mail sender everywhere  
37328 51 d 5 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/ Fetch a fresh EWB auth token when the cached one is stale

Two defects made an expired EWB token unrecoverable, surfacing as GSP102
"eInvoice AuthToken not found or expired" on every GET.

1. ewbApiGet's retry was unreachable. NIC returns GSP102 with HTTP 400, and
RestClient.execute throws GE_1005 on any non-2xx before returning the body, so the
'if (response.contains("GSP102"))' branch could never run. A failed first attempt is
now treated as a possibly-stale token and retried once with a freshly minted one; a
second failure propagates. The POST path was unaffected — executeJson does not check
status, so its retry already worked.

2. Eviction targeted the wrong cache. The token is cached in redisCacheManager but all
three sites evicted through redisFortnightlyCacheManage, a different cache, so the
eviction was a silent no-op and the stale token survived. Eviction now goes through
GstProAuthService.evictEwbAuthToken, declared beside the @Cacheable and pinned to the
same cacheManager so the two cannot drift apart again.

Found while dry-running the EWB backfill, which failed on the first invoice.
 
37323 51 d 8 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/ Drop irn_attempt_count; IRN transport retry is unbounded

Removes the attempt counter added in r37322 along with its pending ALTER TABLE, so the
change no longer carries a schema dependency.

NIC outages resolve within the day, and an invoice legally requires an IRN, so capping
the retry would not remove the obligation — it would only stop trying. Transport failures
now stay queued (irn_generated NULL) until the provider recovers; only a genuine rejection
from NIC is terminal. The failure reason is still recorded in irn_error_message, which
distinguishes a requeued transport failure from an invoice never yet attempted.
 
37322 51 d 9 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/ Stop treating IRN transport timeouts as terminal; isolate NIC calls from batch transaction

A read timeout to GSTPro/NIC was recorded as a final verdict (irn_generated=false),
so 79 invoices billed on 2026-08-17 were left permanently without an IRN even though
NIC may well have filed them. A timeout means the call never completed, not that the
document was rejected.

- markEInvoiceFailed -> recordIrnFailure(invoiceNumber, Throwable): transport failures
leave irn_generated NULL so the cron retries (DUPIRN recovers anything NIC did file);
only a genuine rejection is terminal. Alert email now fires only when terminal.
- New einvoice_details.irn_attempt_count bounds that retry at 10 attempts, reset on
success, so a prolonged NIC outage still converges instead of looping forever.
Requires the matching ALTER TABLE before deploy.
- New saveInvoiceInNewTransaction(invoiceNumber): REQUIRES_NEW per invoice, reloading
orders inside it. RunOnceTasks has class-level @Transactional wrapping the whole
batch loop, so every NIC call previously ran inside one transaction holding write
locks on all orders in the batch; at 60s per call that window is unacceptable.
updateIrnsToInvoices and regenerateBilledInvoices now carry only invoice numbers,
keeping the batch transaction read-only.
- Route all NIC calls (IRN gen, auth, cancel, EWB) through the 60s regulator profile
via GstProAuthService.nicRestClient(). getGstDetails stays on the 10s default since
it runs on request threads.
 
37290 57 d 22 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/ Credit Note IRN: credit only the returned units, not the whole invoice

refundOrder persisted the CreditNote and its lines from the returned
inventory items, but then called generateCreditNoteIrn(invoiceNumber, ...),
which rebuilt the NIC payload by re-reading the original invoice - every
line at billed quantity. A 1-pc DOA return against NSDL37092 therefore
tried to register a CN covering the full invoice (2 phones + 27 carry
bags, Rs 26,664.27) against a Rs 13,332 wallet refund, and the legacy
4-digit HSN on the carry-bag line failed NIC validation with error 2311.

The wallet credit, the CreditNote row and its lines were always correct;
only the e-invoice payload was wrong.

- InvoiceService.getInvoicePdfModelForIrn: optional returnedQtyByOrderId
restricts the item list to the returned orders and prices each line at
the returned quantity. The existing single-arg method delegates with
null, so invoice PDF generation is unchanged. Margin-scheme, delivery
challan, IMEI-suffix and HSN handling apply to the reduced line as-is.
- GstProService.generateCreditNoteIrn: 4-arg overload taking the map; the
3-arg version delegates with null for whole-invoice returns.
- PurchaseReturnServiceImpl.refundOrder: passes orderReturnQtyMap, which
was already built for the ReturnOrderInfo rows.

Verified against the NIC sandbox using the real NSDL37092 rows: the old
payload is rejected with error 2311, the new payload is accepted
(DocTyp CRN, ItemCnt 1, MainHsnCode 85171300, TotInvVal 13331.99).

Not covered here: applyInvoiceReturnViaCreditNote still sends the whole
invoice, so an invoice partially returned earlier re-credits those units.
 

Show All