<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>SmartDukaan &#x2013; /</title><description>WebSVN RSS feed &#x2013; SmartDukaan</description><lastBuildDate>Sat, 29 Aug 2026 08:33:07 +0530</lastBuildDate><generator>WebSVN 2.8.6-DEV</generator><language>en</language><link>https://svn.smartdukaan.com/log.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;max=40&amp;</link><atom:link href="https://svn.smartdukaan.com/rss.php?isdir=1&amp;path=%2F&amp;repname=SmartDukaan" rel="self" type="application/rss+xml" />
<item><pubDate>Sat, 29 Aug 2026 07:49:43 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37457 – Lock the KYC state to the GSTIN on the LOI ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 4 file(s) modified&lt;/strong&gt;&lt;br/&gt;Lock the KYC state to the GSTIN on the LOI&lt;br /&gt;
&lt;br /&gt;
The LOI already captures and validates a GSTIN, and the first two characters of a&lt;br /&gt;
GSTIN are its GST state code - so once the LOI exists the KYC state is a fact&lt;br /&gt;
about that number, not something to re-type. When one is present the state&lt;br /&gt;
dropdown is prefilled from it and locked; otherwise it stays a normal&lt;br /&gt;
master-backed select.&lt;br /&gt;
&lt;br /&gt;
Derived from the GSTIN rather than loi_form.gstState: that column is typed by&lt;br /&gt;
hand and holds values like &apos;UP&apos;, &apos;UTTAR PARDESH&apos; and &apos;MADHAY PARDESH&apos; which&lt;br /&gt;
resolve against nothing, and it is blank on 3 rows. Checked against prod - all&lt;br /&gt;
693 GSTINs on record are 15 characters and every one resolves through&lt;br /&gt;
statemaster.stateCode, and in all 14 rows where the two disagree the GSTIN is&lt;br /&gt;
right and the typed value is wrong or empty.&lt;br /&gt;
&lt;br /&gt;
The lock is per row, not per page: the KYC screens list many onboardings, so the&lt;br /&gt;
derived state is passed as a map keyed by onboarding id. Blank rows carry their&lt;br /&gt;
onboarding id so a new KYC entry is locked too, which is the case that matters.&lt;br /&gt;
&lt;br /&gt;
data-gst-locked keeps the row&apos;s dblclick edit toggle from re-enabling the field;&lt;br /&gt;
the value still submits because the JS reads it explicitly with .val().&lt;/div&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/PartnerOnBoardingPanelController.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/resources/js/panel-listing.js&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/fofo-kyc-row.vm&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/legal-kyc-row.vm&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37457</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37457</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:42:55 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37456 – Block billing when NIC rejects the transporter GSTIN for e-way ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 6 file(s) modified&lt;/strong&gt;&lt;br/&gt;Block billing when NIC rejects the transporter GSTIN for e-way bills&lt;br /&gt;
&lt;br /&gt;
NIC files the e-way bill alongside the IRN, so a deregistered transporter&lt;br /&gt;
GSTIN returns Status=1 with InfoDtls[InfCd=EWBERR] (3029 &quot;GSTIN - ... is&lt;br /&gt;
not active&quot;): the IRN is valid while ewb_no stays null. Nothing downstream&lt;br /&gt;
reads that as a failure, so invoices kept being issued for goods that&lt;br /&gt;
could not legally move.&lt;br /&gt;
&lt;br /&gt;
Cache the rejected transporter GSTIN in Redis and refuse to bill through&lt;br /&gt;
it. The block is keyed on the GSTIN, since one GSTIN is shared by several&lt;br /&gt;
warehouse_provider rows, and it carries the day it was raised so it lapses&lt;br /&gt;
at midnight and each new day re-tests NIC once. Correcting the GSTIN in&lt;br /&gt;
the provider panel lifts it immediately.&lt;br /&gt;
&lt;br /&gt;
Only errors that are the transporter&apos;s fault block billing - NIC&apos;s 3029,&lt;br /&gt;
or any message naming the GSTIN we sent as TransId. Every other EWBERR&lt;br /&gt;
behaves as before: the IRN is filed and the e-way bill is retried later.&lt;br /&gt;
&lt;br /&gt;
Guard sits in addBillingDetailsForGrouppedOrders before the pessimistic&lt;br /&gt;
lock and before any mutation, mirroring LogisticsServiceImpl#getEwbDetails&lt;br /&gt;
(order&apos;s own warehouse; self-pickup and runner skipped, as they travel on&lt;br /&gt;
a vehicle number rather than a transporter id).&lt;/div&gt;~ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/model/ProfitMandiConstants.java&lt;br /&gt;+ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/EwbTransporterFailureCache.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/GstProService.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/transaction/TransactionServiceImpl.java&lt;br /&gt;+ /trunk/profitmandi-dao/src/test/java/com/spice/profitmandi/service/integrations/gstpro/EwbTransporterFailureCacheTest.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/provider/ProviderController.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37456</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37456</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:31:14 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37455 – Make the KYC state a master-backed dropdown instead of free ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 5 file(s) modified&lt;/strong&gt;&lt;br/&gt;Make the KYC state a master-backed dropdown instead of free text&lt;br /&gt;
&lt;br /&gt;
fofo-kyc-row.vm and legal-kyc-row.vm captured state as a free-text input, so&lt;br /&gt;
nothing stopped a spelling the master does not hold. That state is resolved back&lt;br /&gt;
through inventory.statemaster by name later, and an unresolvable one cannot be&lt;br /&gt;
turned into a state code. Both now render a $stateNames select, and all five&lt;br /&gt;
render paths in PartnerOnBoardingPanelController supply the list.&lt;br /&gt;
&lt;br /&gt;
panel-listing.js reads these fields positionally, by td index and&lt;br /&gt;
input[type=&apos;text&apos;]. A select does not match that selector, so the value would&lt;br /&gt;
have come back undefined and KYC would have saved a blank state silently -&lt;br /&gt;
both selectors now target the select. Row editing is unaffected: the enable&lt;br /&gt;
toggle uses :input, which covers select.&lt;br /&gt;
&lt;br /&gt;
Bump the asset version for the panel-listing.js change.&lt;/div&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/config/AppConfig.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/PartnerOnBoardingPanelController.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/resources/js/panel-listing.js&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/fofo-kyc-row.vm&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/legal-kyc-row.vm&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37455</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37455</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:24:40 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37454 – Build googleMailSender as AuthenticatedIdentityMailSender  The bean authenticates as sdtech@smartdukaan.com, ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Build googleMailSender as AuthenticatedIdentityMailSender&lt;br /&gt;
&lt;br /&gt;
The bean authenticates as &lt;a href=&quot;mailto:sdtech@smartdukaan.com&quot;&gt;sdtech@smartdukaan.com&lt;/a&gt;, so Google Workspace only permits&lt;br /&gt;
that address in From. Callers hardcode noreply@, which Gmail refuses with 535&lt;br /&gt;
&apos;Authenticated user is not authorized to send mail&apos;, silently dropping every alert&lt;br /&gt;
sent through it. The sender now rewrites From to its own username at send time.&lt;br /&gt;
&lt;br /&gt;
See profitmandi-common r37451.&lt;/div&gt;~ /trunk/profitmandi-web/src/main/java/com/spice/profitmandi/web/config/AppConfig.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37454</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37454</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:24:39 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37453 – Build googleMailSender as AuthenticatedIdentityMailSender  The bean authenticates as sdtech@smartdukaan.com, ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Build googleMailSender as AuthenticatedIdentityMailSender&lt;br /&gt;
&lt;br /&gt;
The bean authenticates as &lt;a href=&quot;mailto:sdtech@smartdukaan.com&quot;&gt;sdtech@smartdukaan.com&lt;/a&gt;, so Google Workspace only permits&lt;br /&gt;
that address in From. Callers hardcode noreply@, which Gmail refuses with 535&lt;br /&gt;
&apos;Authenticated user is not authorized to send mail&apos;, silently dropping every alert&lt;br /&gt;
sent through it. The sender now rewrites From to its own username at send time.&lt;br /&gt;
&lt;br /&gt;
See profitmandi-common r37451.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/Application.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37453</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37453</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:24:31 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37452 – Fix e-invoice IRN failure when the NIC auth token is ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;Fix e-invoice IRN failure when the NIC auth token is about to expire&lt;br /&gt;
&lt;br /&gt;
NIC reissues the same auth token until it genuinely expires, so evicting the cache&lt;br /&gt;
inside the 5 minute leeway hands back the identical near-dead token. getAuthenticatedHeaders&lt;br /&gt;
checked the leeway once, before that re-fetch, and never inspected what came back.&lt;br /&gt;
NSUPHR1770 filed with a token that had 12 seconds of life left and the gateway answered&lt;br /&gt;
412/GSP752 &apos;eInvoice AuthToken not found or expired&apos;; the invoice filed a minute later,&lt;br /&gt;
past the expiry and so on a genuinely new token, succeeded.&lt;br /&gt;
&lt;br /&gt;
getUsableAuthToken re-checks the refetched token and, when it is still inside a 60s&lt;br /&gt;
floor, waits out the remainder before minting again. The wait is bounded by that floor&lt;br /&gt;
and every caller on this path is a cron/async thread already running against a 60s NIC&lt;br /&gt;
timeout with no transaction held open.&lt;br /&gt;
&lt;br /&gt;
Also make the gateway&apos;s own failures legible. NIC rejections arrive as&lt;br /&gt;
{Status, ErrorDetails}, but failures the ASP raises in front of NIC use&lt;br /&gt;
{status_cd, error{error_cd, message}}, which shares no field name with RespPl: Gson&lt;br /&gt;
produced an all-null object and the stored reason degraded to the literal&lt;br /&gt;
&apos;RespPl{Status=0, Data=null, ErrorDetails=null, InfoDtls=null}&apos;, indistinguishable from&lt;br /&gt;
NIC rejecting the document. RespGSPErr already modelled that envelope but was a&lt;br /&gt;
non-static inner class and so could not be instantiated by Gson; made it static and&lt;br /&gt;
copy the code and message into ErrorDetails, which every caller already reads.&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/api/model/E_InvoiceCommon.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/GstProAuthService.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37452</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37452</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:24:19 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37451 – Send authenticated mail as the account that actually authenticates  ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Send authenticated mail as the account that actually authenticates&lt;br /&gt;
&lt;br /&gt;
googleMailSender logs in to smtp.gmail.com as &lt;a href=&quot;mailto:sdtech@smartdukaan.com&quot;&gt;sdtech@smartdukaan.com&lt;/a&gt;, but callers&lt;br /&gt;
build their From as &lt;a href=&quot;mailto:noreply@smartdukaan.com&quot;&gt;noreply@smartdukaan.com&lt;/a&gt;. Google Workspace binds an authenticated&lt;br /&gt;
session to one identity and refuses the mismatch with 535 &apos;Authorization failed:&lt;br /&gt;
Authenticated user is not authorized to send mail&apos; - the password is accepted, the&lt;br /&gt;
message is not. Every alert routed through that sender was therefore lost at the SMTP&lt;br /&gt;
layer, the e-invoice failure alert for NSUPHR1770 among them.&lt;br /&gt;
&lt;br /&gt;
AuthenticatedIdentityMailSender rewrites From to the sender&apos;s own username at send&lt;br /&gt;
time, keeping whatever display name the caller chose. Senders that do not authenticate&lt;br /&gt;
are left alone: gmailRelaySender is the IP-authorised relay and has no username, and&lt;br /&gt;
sendGridMailSender&apos;s username is the literal &apos;apikey&apos;, so both keep noreply@.&lt;br /&gt;
&lt;br /&gt;
Enforced in the sender rather than at the ~40 call sites that build their own&lt;br /&gt;
MimeMessage and hardcode noreply@, so it covers the ones added next too.&lt;/div&gt;+ /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/services/AuthenticatedIdentityMailSender.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37451</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37451</guid></item>
<item><pubDate>Sat, 29 Aug 2026 07:17:00 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37450 – Oppo: find only the hole, and let a low-contrast hole ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo: find only the hole, and let a low-contrast hole be found at all&lt;br /&gt;
&lt;br /&gt;
60% of oppo attempts never got past detection -- getMatCircles2 returned null and&lt;br /&gt;
the attempt was wasted before any aiming happened. That is a bigger loss than&lt;br /&gt;
everything the scheduling work addressed put together.&lt;br /&gt;
&lt;br /&gt;
Two causes, both visible once real puzzles are captured and looked at.&lt;br /&gt;
&lt;br /&gt;
1. param1 is the Canny HIGH threshold, and it was 100. Edges weaker than that are&lt;br /&gt;
   discarded before circle finding begins. Measured hole-vs-background contrast on&lt;br /&gt;
   four live puzzles: 98, 103, 28, 117. The 28 -- a pale lilac background -- cannot&lt;br /&gt;
   produce an edge at 100, so no circle exists to find. Dropped to 50.&lt;br /&gt;
&lt;br /&gt;
2. It insisted on TWO circles whose radii matched within 3px, and returned null&lt;br /&gt;
   otherwise. So a puzzle where the hole was located perfectly still failed if the&lt;br /&gt;
   ring was missed, or if the two radii differed by 4px.&lt;br /&gt;
&lt;br /&gt;
The second is the sillier one: the ring is a DOM element,&lt;br /&gt;
dx_captcha_basic_sub-slider_, whose exact position aim() already reads via&lt;br /&gt;
pieceCentreX(). Detection was re-finding something known exactly, and then&lt;br /&gt;
throwing away a good hole because it could not confirm it. Now the ring position&lt;br /&gt;
is passed in and hough only has to find one thing.&lt;br /&gt;
&lt;br /&gt;
Lowering param1 without the pairing check would let textured backgrounds (the&lt;br /&gt;
sand images especially) supply spurious circles with nothing to reject them, so&lt;br /&gt;
the candidate is verified as an actual hole: its core must be at least 15&lt;br /&gt;
luminance below the frame mean. Real holes measure 28-117 below, so 15 rejects&lt;br /&gt;
noise with margin.&lt;br /&gt;
&lt;br /&gt;
Validated against live puzzles before committing:&lt;br /&gt;
  ring is at rel 42px on every sample; hole lands at 113-194px, separation&lt;br /&gt;
  71-152px -- so RING_EXCLUSION_PX = 30 never rejects a real hole&lt;br /&gt;
  hole contrast 28-117 against MIN_HOLE_DARKNESS = 15&lt;br /&gt;
&lt;br /&gt;
Not validated locally: the hough call itself. opencv 3.4.2-0 ships no osx/arm64&lt;br /&gt;
native and every JDK on this machine is arm64, so the detection path cannot run&lt;br /&gt;
here. param1 = 50 is reasoned from the measured contrasts, not measured. If the&lt;br /&gt;
detection rate does not move, 40 is the next value to try -- the darkness check is&lt;br /&gt;
what makes going lower safe.&lt;br /&gt;
&lt;br /&gt;
Watch &quot;Detected N circle(s) but all sat on the ring&quot; and &quot;Rejecting circle at Npx&quot;&lt;br /&gt;
in the log: the first means hough found nothing but the ring, the second means the&lt;br /&gt;
darkness check is doing its job.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37450</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37450</guid></item>
<item><pubDate>Fri, 28 Aug 2026 20:25:12 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37449 – Realme unmerged too: four selenium jobs, one per pool per ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Realme unmerged too: four selenium jobs, one per pool per brand&lt;br /&gt;
&lt;br /&gt;
r37447 merged secondary and tertiary per brand to halve concurrent drivers.&lt;br /&gt;
r37448 reverted that for oppo, because serialising cost it more per imei than a&lt;br /&gt;
second browser did. This reverts it for realme as well, which ends the merge&lt;br /&gt;
experiment entirely.&lt;br /&gt;
&lt;br /&gt;
What the merge actually cost realme is only visible now that oppo is parallel&lt;br /&gt;
again: realme&apos;s per-imei went 14.2s -&gt; 29s. It did not change behaviour -- it is&lt;br /&gt;
simply competing with oppo&apos;s two browsers for CPU. At 29s its merged ceiling is&lt;br /&gt;
86400/29 = 2,979/day, just under the 3,054/day the daily re-check needs, so no&lt;br /&gt;
batch size could have closed the gap. Two parallel pools restore the ~3,900/day&lt;br /&gt;
it managed historically at four drivers.&lt;br /&gt;
&lt;br /&gt;
Measured before this change (25 min window):&lt;br /&gt;
&lt;br /&gt;
  brand    needed/day   throughput   note&lt;br /&gt;
  Oppo         8,838       7,661     parallel revert worked, +122%&lt;br /&gt;
  Realme       3,054       1,843     merged and CPU-starved&lt;br /&gt;
  Vivo        10,082      16,128     surplus, cannot transfer to another brand&lt;br /&gt;
&lt;br /&gt;
Sizes unchanged: oppo 25 per pool, realme 12 per pool, vivo 50+10.&lt;br /&gt;
&lt;br /&gt;
Honest accounting of the merge: it was my idea, sized on per-brand arithmetic that&lt;br /&gt;
ignored contention between brands, and it is now fully reverted. What survives&lt;br /&gt;
from that line of work is the part that actually paid -- reaping orphaned browsers&lt;br /&gt;
(~1.8GB), the per-brand retry caps, and per-brand maxResults. Peak drivers are&lt;br /&gt;
back to 4, which is where they started.&lt;br /&gt;
&lt;br /&gt;
Watch for contention: four browsers is the configuration that produced the 29s&lt;br /&gt;
figure for realme in the first place, so oppo may slow from its current 19.7s.&lt;br /&gt;
Re-measure both before tuning sizes again.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ScheduledSkeleton.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37449</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37449</guid></item>
<item><pubDate>Fri, 28 Aug 2026 12:08:39 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37448 – Daily re-check for all brands; Oppo back to parallel pools ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 3 file(s) modified&lt;/strong&gt;&lt;br/&gt;Daily re-check for all brands; Oppo back to parallel pools&lt;br /&gt;
&lt;br /&gt;
Two changes.&lt;br /&gt;
&lt;br /&gt;
1. Re-check window 4 days (secondary) and 2 days (tertiary) -&gt; 1 day everywhere.&lt;br /&gt;
   Daily demand becomes the full universe rather than a fraction of it:&lt;br /&gt;
&lt;br /&gt;
     Oppo    4,798 + 4,040 =  8,838/day&lt;br /&gt;
     Vivo    9,407 +   675 = 10,082/day   (doing 15,345 -- fine)&lt;br /&gt;
     Realme  1,973 + 1,081 =  3,054/day&lt;br /&gt;
&lt;br /&gt;
2. Oppo&apos;s two pools run in PARALLEL again, reverting the merge in r37447 for that&lt;br /&gt;
   brand only. Realme stays merged.&lt;br /&gt;
&lt;br /&gt;
   The merge was a straight trade of throughput for memory and oppo could not&lt;br /&gt;
   afford it. Measured over 32 minutes and again over an hour the next morning:&lt;br /&gt;
   3,555 then 3,456/day against 5,280 before merging. Batch cadence settled at a&lt;br /&gt;
   very regular ~15.5 min per cycle, so a 30-imei merged batch takes ~10.5 min =&lt;br /&gt;
   ~21s/imei, against the 10.2s it managed unmerged. At 21s the ceiling is&lt;br /&gt;
   86400/21 = 4,114/day even with zero idle, so no batch size and no shorter&lt;br /&gt;
   fixedDelay could have reached 8,838. Serialising simply costs more per imei&lt;br /&gt;
   here than running two browsers does.&lt;br /&gt;
&lt;br /&gt;
   Realme keeps the merge: it needs 3,054/day and delivers 2,952 merged, so a&lt;br /&gt;
   small size bump covers it without a second browser.&lt;br /&gt;
&lt;br /&gt;
Sizes: oppo 25 per pool (2 jobs in parallel), realme 12+12 merged, vivo 50+10&lt;br /&gt;
unchanged. Vivo has already cleared its entire secondary backlog -- the pool&lt;br /&gt;
reads 0 and both lists come back empty -- which is what the batch of 50 was for.&lt;br /&gt;
&lt;br /&gt;
Cost: oppo goes back to two concurrent drivers, so the fleet is 3 rather than 2,&lt;br /&gt;
roughly +700MB. Acceptable against the ~2GB freed today by reaping orphaned&lt;br /&gt;
browsers and capping retries, but it is the reason realme was left merged.&lt;br /&gt;
&lt;br /&gt;
Sizes are a starting point, not a final answer: oppo&apos;s per-imei time differs&lt;br /&gt;
markedly between merged and parallel modes, so re-measure before tuning further.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ScheduledSkeleton.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/StandAlone.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/VivoImeiActivationService.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37448</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37448</guid></item>
<item><pubDate>Thu, 27 Aug 2026 20:36:36 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37447 – Selenium: one browser per brand instead of one per pool ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;Selenium: one browser per brand instead of one per pool&lt;br /&gt;
&lt;br /&gt;
Oppo and Realme each ran secondary and tertiary as separate @Scheduled jobs, so&lt;br /&gt;
each brand opened two ChromeDrivers at once and the fleet ran four. Each driver&lt;br /&gt;
tree costs roughly 850MB. This box co-hosts a 9.4GB tomcat with ~3GB available&lt;br /&gt;
and has been OOM-killed twice this month -- tomcat was the victim both times, so&lt;br /&gt;
peak concurrency is the thing that matters here.&lt;br /&gt;
&lt;br /&gt;
Combined into one job per brand. Nothing downstream changes: the two pools differ&lt;br /&gt;
only in which named query fills them, and both already funnel into the same&lt;br /&gt;
updateActivationDate -&gt; checkWarranty -&gt; saveActivation path. They are disjoint by&lt;br /&gt;
construction (secondary excludes anything with a FofoLineItem, tertiary is&lt;br /&gt;
FofoLineItem-based); distinct() is insurance, not a fix for a known overlap.&lt;br /&gt;
&lt;br /&gt;
Sizing matters, because merging SERIALISES work that used to run in parallel and&lt;br /&gt;
keeping the old batch sizes would quietly cost throughput. Measured post-cap at&lt;br /&gt;
10.2s/imei (oppo, down from 14.6 after r37445) and 14.2s/imei (realme), solving&lt;br /&gt;
M * 86400 / (300 + M*t):&lt;br /&gt;
&lt;br /&gt;
  oppo    2 parallel jobs x10 = 4,299/day  -&gt;  merged 15+15 = 4,277/day  (parity)&lt;br /&gt;
  realme  2 parallel jobs x10 = 3,910/day  -&gt;  merged 10+10 = 2,959/day  (-24%)&lt;br /&gt;
&lt;br /&gt;
Oppo is sized to hold parity because it is already short of its 4,798/day need.&lt;br /&gt;
Realme is left at 20 -- it needs 2,243/day, so it can absorb the dip in exchange&lt;br /&gt;
for shorter batches and a shorter-lived browser.&lt;br /&gt;
&lt;br /&gt;
What this saves and does not save: total driver-SECONDS are roughly unchanged,&lt;br /&gt;
which is the point of resizing. PEAK concurrent drivers halves from 4 to 2.&lt;br /&gt;
&lt;br /&gt;
Also skips starting a browser at all when both pools come back empty -- currently&lt;br /&gt;
never true, but it costs nothing and a browser launched to do nothing is pure&lt;br /&gt;
waste on this box.&lt;br /&gt;
&lt;br /&gt;
checkOppoImeiStatus/Tertiary and the realme equivalents are left in place for&lt;br /&gt;
manual invocation; they are simply no longer scheduled.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ScheduledSkeleton.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/StandAlone.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37447</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37447</guid></item>
<item><pubDate>Thu, 27 Aug 2026 20:27:13 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37446 – Per-brand batch sizes, and stop chrome forking a GPU process ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 6 file(s) modified&lt;/strong&gt;&lt;br/&gt;Per-brand batch sizes, and stop chrome forking a GPU process it cannot use&lt;br /&gt;
&lt;br /&gt;
maxResults was hardcoded in the shared repository methods, so Oppo and Vivo were&lt;br /&gt;
forced to the same secondary batch (10) and all three to the same tertiary (10).&lt;br /&gt;
It is now a parameter, set per brand at the call site.&lt;br /&gt;
&lt;br /&gt;
Sizing is arithmetic, from measured IN-BATCH per-imei time. Solving&lt;br /&gt;
M * 86400 / (300 + M*t) = needed/day:&lt;br /&gt;
&lt;br /&gt;
  brand    needed/day   t       M required   set to&lt;br /&gt;
  Vivo         9,407    0.8s        36         50    clears, ~12,700/day&lt;br /&gt;
  Realme       1,973   13.4s        10         10    was 5 = ~1,177/day, short&lt;br /&gt;
  Oppo         4,798   14.6s        88         10    HELD, see below&lt;br /&gt;
&lt;br /&gt;
Correcting an earlier measurement of mine: I reported Vivo at 13.6s per imei and&lt;br /&gt;
concluded its backlog could not be cleared. That averaged across the ~300s idle&lt;br /&gt;
gaps BETWEEN batches. In-batch it is 0.8s -- Vivo is 17x faster than I said, is&lt;br /&gt;
idle ~97% of the time, and 50 clears its pool comfortably. There is no wait in&lt;br /&gt;
the Vivo path; it is simply fast.&lt;br /&gt;
&lt;br /&gt;
Oppo is deliberately NOT raised. At 14.6s it would need M=88, which means&lt;br /&gt;
20-minute batches and near-permanent chrome sessions. But that 14.6s predates the&lt;br /&gt;
retry cap (r37445), which cuts exhausted imeis from 20 attempts to 7 and should&lt;br /&gt;
drop it sharply. Re-measure before sizing Oppo, rather than guessing high on a&lt;br /&gt;
box with 3GB free.&lt;br /&gt;
&lt;br /&gt;
Also: --disable-gpu, --disable-dev-shm-usage, --disable-software-rasterizer on&lt;br /&gt;
both selenium tasks. Headless needs no GPU yet chrome forks a gpu-process per&lt;br /&gt;
browser -- 6 were alive across the fleet, pure overhead. No behaviour change.&lt;br /&gt;
&lt;br /&gt;
Batch size does not raise peak concurrency (fixedDelay means one batch per job at&lt;br /&gt;
a time, so never more than 4 drivers). It raises DUTY CYCLE, which converts&lt;br /&gt;
chrome&apos;s footprint from intermittent to sustained. That matters here: tomcat is&lt;br /&gt;
9.4GB, available is ~3GB, and the two OOM kills this month both took tomcat.&lt;br /&gt;
&lt;br /&gt;
Cron-only deploy. The dao signature change has no callers outside cron.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckRealmeWarrantyTask.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/StandAlone.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/VivoImeiActivationService.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/repository/fofo/ActivatedImeiRepository.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/repository/fofo/ActivatedImeiRepositoryImpl.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37446</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37446</guid></item>
<item><pubDate>Thu, 27 Aug 2026 18:47:45 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37445 – Oppo/Realme: cap captcha retries per tick at 7 and 12, ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo/Realme: cap captcha retries per tick at 7 and 12, measured per brand&lt;br /&gt;
&lt;br /&gt;
Most captcha work goes to imeis that never succeed. Measured over the last&lt;br /&gt;
window: oppo 6,245 attempts for 308 successes, realme 13,842 for 399. The bulk&lt;br /&gt;
is imeis grinding all 20 attempts in one already-refused session and getting&lt;br /&gt;
nothing -- oppo 279 of them, realme 610.&lt;br /&gt;
&lt;br /&gt;
The imei is NOT abandoned. A captcha failure is a technical failure, not an&lt;br /&gt;
answer, so it stays pending and the next tick retries it in ~5 minutes with a&lt;br /&gt;
fresh page and session -- which the data suggests beats continuing in a session&lt;br /&gt;
the widget has refused seven times. This caps grinding, not retrying.&lt;br /&gt;
&lt;br /&gt;
Per-brand caps, because the distributions genuinely differ:&lt;br /&gt;
&lt;br /&gt;
  OPPO cap 7    97.7% of successes kept, 58.4% of work saved (3,648 attempts)&lt;br /&gt;
    Pre-glide, 33% of oppo&apos;s successes came from attempts 8-20 and 20 was the&lt;br /&gt;
    right number. Post-glide (r37440) that is 2%: the drag lands first or second&lt;br /&gt;
    try. cap 5 keeps 95.5%/saves 67.7%, cap 10 keeps 99.0%/saves 44.8%.&lt;br /&gt;
&lt;br /&gt;
  REALME cap 12  96.0% kept, 35.7% saved (4,937 attempts)&lt;br /&gt;
    NOT 7. Realme has no glide, so its successes still spread to attempt 10+ and&lt;br /&gt;
    a cap of 7 would cost it 16.5%. It also burns more than twice oppo&apos;s work, so&lt;br /&gt;
    the gentler cap still saves more in absolute terms.&lt;br /&gt;
&lt;br /&gt;
Combined: 8,585 of 20,087 attempts saved, ~43% less browser work, for ~3% fewer&lt;br /&gt;
successes per tick -- and those imeis come back next tick anyway. Less Chrome&lt;br /&gt;
work matters on this box: it co-hosts a 9.4GB tomcat, has 3.5GB free and has been&lt;br /&gt;
OOM-killed twice this month.&lt;br /&gt;
&lt;br /&gt;
The two caps are independent constants and must not be synced. If glide is ported&lt;br /&gt;
to realme, expect its curve to shift left as oppo&apos;s did and its cap can drop to&lt;br /&gt;
~7 -- but measure it, do not assume it.&lt;br /&gt;
&lt;br /&gt;
Windows are ~2h (oppo&apos;s only ~30 min post-glide), so the right numbers may drift.&lt;br /&gt;
Worth re-reading the attempt histogram after a full day.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckRealmeWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37445</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37445</guid></item>
<item><pubDate>Thu, 27 Aug 2026 17:36:31 +0530</pubDate><dc:creator>ranu</dc:creator><title>Rev 37444 – rbm drr dashboard new ui looks</title><description>&lt;div&gt;&lt;strong&gt;ranu – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;rbm drr dashboard new ui looks&lt;/div&gt;+ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/rbm-drr-dashboard.vm&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37444</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37444</guid></item>
<item><pubDate>Thu, 27 Aug 2026 17:33:30 +0530</pubDate><dc:creator>ranu</dc:creator><title>Rev 37443 – rbm drr dashboard new ui looks</title><description>&lt;div&gt;&lt;strong&gt;ranu – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;rbm drr dashboard new ui looks&lt;/div&gt;+ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/model/RbmBrandSoldModel.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37443</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37443</guid></item>
<item><pubDate>Thu, 27 Aug 2026 17:28:05 +0530</pubDate><dc:creator>ranu</dc:creator><title>Rev 37442 – rbm drr dashboard new ui looks</title><description>&lt;div&gt;&lt;strong&gt;ranu – 5 file(s) modified&lt;/strong&gt;&lt;br/&gt;rbm drr dashboard new ui looks&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/RbmTargetService.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/RbmTargetServiceImpl.java&lt;br /&gt;+ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/monitors/RbmDrrDashboardController.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/rbm-today-arr.vm&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/today_po_rbm.vm&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37442</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37442</guid></item>
<item><pubDate>Thu, 27 Aug 2026 17:02:33 +0530</pubDate><dc:creator>ranu</dc:creator><title>Rev 37441 – bi access to gaurav mathur</title><description>&lt;div&gt;&lt;strong&gt;ranu – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;bi access to gaurav mathur&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ScheduledTasksTest.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37441</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37441</guid></item>
<item><pubDate>Thu, 27 Aug 2026 13:27:35 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37440 – Oppo: slide the drag in eased sub-moves instead of jumping ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo: slide the drag in eased sub-moves instead of jumping&lt;br /&gt;
&lt;br /&gt;
Aim is solved and it did not finish the job: r37438 lands the ring 0-1px from the&lt;br /&gt;
hole on 103 of 104 drags and oppo still converts ~15%, with POSITION_MISMATCH on&lt;br /&gt;
the rest. So the widget is refusing drags that end in the right place.&lt;br /&gt;
&lt;br /&gt;
Dingxiang scores trajectory and velocity as well as final position and returns the&lt;br /&gt;
same POSITION_MISMATCH for both, so our verdict cannot separate &quot;wrong place&quot; from&lt;br /&gt;
&quot;wrong-looking movement&quot;. The circumstantial case for the latter:&lt;br /&gt;
&lt;br /&gt;
  oppo    0.91s hold, 2-3 discrete jumps   -&gt; 15%&lt;br /&gt;
  realme  1.93s hold, slower and more      -&gt; 54-74%&lt;br /&gt;
&lt;br /&gt;
and r37438 made oppo FASTER by removing the per-step screenshots, moving it further&lt;br /&gt;
from realme&apos;s profile rather than closer.&lt;br /&gt;
&lt;br /&gt;
glide() traverses each leg as up to 18 small sub-moves on an ease-out curve&lt;br /&gt;
(1-(1-t)^2, quick then settling) with a 45ms pause between, so the pointer&lt;br /&gt;
describes a continuous decelerating path instead of teleporting.&lt;br /&gt;
&lt;br /&gt;
Nothing about the DISTANCE changes -- it is still exactly what the derived ratio&lt;br /&gt;
says, and verified to land on it precisely: a 116px leg becomes&lt;br /&gt;
[16,15,13,13,11,10,9,8,6,6,4,3,1,1] and sums to 116. Negative legs work the same&lt;br /&gt;
way for overshoot correction.&lt;br /&gt;
&lt;br /&gt;
To be explicit, since this is the thing not to get wrong: the ratio remains fully&lt;br /&gt;
derived. It starts at zero and comes only from measured movement, per attempt and&lt;br /&gt;
per step. Splitting a leg into sub-moves gives MORE measured legs, not fewer, so&lt;br /&gt;
the estimate sharpens rather than being replaced by an assumption. No constant is&lt;br /&gt;
introduced -- MAX_GLIDE_STEPS, GLIDE_PX_PER_STEP and GLIDE_PAUSE_MS describe the&lt;br /&gt;
path, not the relationship between mouse px and ring px.&lt;br /&gt;
&lt;br /&gt;
Expected hold ~1.7s against realme&apos;s 1.93s. Oppo only.&lt;br /&gt;
&lt;br /&gt;
This is a hypothesis test, not a known fix. POSITION_MISMATCH rate answers it in&lt;br /&gt;
one tick; if it does not move, drag dynamics is eliminated and the remaining&lt;br /&gt;
suspect is Hough picking the wrong circle pair on oppo&apos;s imagery, which needs&lt;br /&gt;
visual evidence rather than another code change.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37440</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37440</guid></item>
<item><pubDate>Thu, 27 Aug 2026 13:12:09 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37439 – Oppo: drop the stale nudgeFor javadoc that still referenced ASSUMED_RATIO ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo: drop the stale nudgeFor javadoc that still referenced ASSUMED_RATIO&lt;br /&gt;
&lt;br /&gt;
Comment only, no code change.&lt;br /&gt;
&lt;br /&gt;
nudgeFor() and ASSUMED_RATIO were removed in r37438, but the javadoc describing&lt;br /&gt;
them was left orphaned above ALIGNED_PX, still explaining how to size a probe from&lt;br /&gt;
an assumed px-per-px. That is precisely the thing not to reintroduce, and a stale&lt;br /&gt;
comment arguing for it is an invitation to do so.&lt;br /&gt;
&lt;br /&gt;
Replaced with a note on why there is no assumed ratio: it is derived from real&lt;br /&gt;
movement on every attempt and re-derived on every step within it. The constant was&lt;br /&gt;
tuned twice (1.7 -&gt; 0.67 -&gt; 1.7) against measurements contaminated by the leaked&lt;br /&gt;
slider press, and never helped. The widget&apos;s px-per-px genuinely varies by&lt;br /&gt;
environment (1.4-1.5 in production, 1.1-1.2 locally) and by brand, which is the&lt;br /&gt;
reason it has to be measured rather than assumed.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37439</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37439</guid></item>
<item><pubDate>Thu, 27 Aug 2026 06:38:15 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37438 – Oppo: aim by tracking the ring element, not by re-detecting ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo: aim by tracking the ring element, not by re-detecting circles&lt;br /&gt;
&lt;br /&gt;
r37437&apos;s closed loop could not close. Production, 13 minutes:&lt;br /&gt;
&lt;br /&gt;
  step 0 (probe) ok    36&lt;br /&gt;
  lost the circles     32   (25 of them on step 1, right after the main move)&lt;br /&gt;
  aligned within 2px    0&lt;br /&gt;
&lt;br /&gt;
The probe worked and derived sane ratios (0.75-1.00), then the next measurement&lt;br /&gt;
returned null nearly every time and the loop bailed.&lt;br /&gt;
&lt;br /&gt;
The cause is structural, not a tuning miss. getMatCircles2 needs Hough to resolve&lt;br /&gt;
TWO circles of similar radius, and as the ring closes on the hole they overlap and&lt;br /&gt;
stop being two. The measurement dies exactly at convergence, so no loop built on&lt;br /&gt;
&quot;distance between two detected circles&quot; can ever verify success. The old refine()&lt;br /&gt;
hit the same wall and silently released, which is why it never helped either.&lt;br /&gt;
&lt;br /&gt;
So detection is now used ONCE, for what it is good at -- locating the hole while&lt;br /&gt;
the two are still far apart -- and from then on we track the ring&apos;s OWN element,&lt;br /&gt;
dx_captcha_basic_sub-slider_, whose rect stays readable however close it gets.&lt;br /&gt;
&lt;br /&gt;
Validated on the live widget before committing:&lt;br /&gt;
&lt;br /&gt;
  bg element    page x=556, width 288 CSS&lt;br /&gt;
  ring centre   page 598  -&gt;  42 relative to bg&lt;br /&gt;
  dark target   138 relative to bg  -&gt;  page 694, gap 96px&lt;br /&gt;
&lt;br /&gt;
The ring sits in the same 0-288 CSS space as OpenCV&apos;s x values, so bg.x + circleX&lt;br /&gt;
is directly comparable to the ring&apos;s position. That holds because getMatCircles2&lt;br /&gt;
crops by CSS coordinates and chrome runs --force-device-scale-factor=1. A 96px gap&lt;br /&gt;
matches the range production logs as &quot;Distance is&quot;.&lt;br /&gt;
&lt;br /&gt;
Also validated earlier that the ring&apos;s rect tracks a drag cleanly the whole way in&lt;br /&gt;
(+10 mouse -&gt; +11 ring, repeatably), which is the property the circle detection&lt;br /&gt;
lacks.&lt;br /&gt;
&lt;br /&gt;
The ratio is still derived per move and is now signed, so overshoot corrects itself&lt;br /&gt;
rather than needing to be detected. No screenshot or Hough pass per step either, so&lt;br /&gt;
the drag is quicker as well.&lt;br /&gt;
&lt;br /&gt;
Oppo only. Realme is converting 78% and is not touched.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37438</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37438</guid></item>
<item><pubDate>Thu, 27 Aug 2026 06:25:11 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37437 – Oppo: derive the drag ratio from every move, and survive ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo: derive the drag ratio from every move, and survive a stale slider&lt;br /&gt;
&lt;br /&gt;
Two changes, both oppo only. Realme is converting 82% and is left alone.&lt;br /&gt;
&lt;br /&gt;
1. No assumed ratio -- aim() is a closed loop.&lt;br /&gt;
&lt;br /&gt;
   ASSUMED_RATIO only ever sized the FIRST probe, but the old path then derived&lt;br /&gt;
   px-per-px once from that single probe and trusted it for the main move and the&lt;br /&gt;
   correction alike. refine() re-measured the gap but reused the same ratio and&lt;br /&gt;
   never checked whether its own correction landed, so one noisy probe poisoned&lt;br /&gt;
   everything after it.&lt;br /&gt;
&lt;br /&gt;
   aim() seeds with a small fixed 12px nudge -- small enough that it cannot cross&lt;br /&gt;
   the hole -- then re-derives the ratio from what each move actually achieved and&lt;br /&gt;
   blends it with the running estimate. Up to 5 steps, stopping as soon as the gap&lt;br /&gt;
   is within ALIGNED_PX.&lt;br /&gt;
&lt;br /&gt;
   It can also see overshoot, which a single pass cannot: the gap is |c1 - c2| and&lt;br /&gt;
   cannot tell &quot;not there yet&quot; from &quot;gone past&quot;, but if a move makes the gap GROW&lt;br /&gt;
   we have crossed the hole, and the next step flips sign.&lt;br /&gt;
&lt;br /&gt;
   This removes the constant rather than retuning it, so it is immune to the scale&lt;br /&gt;
   differences we measured between environments -- 1.1-1.2 px per px locally&lt;br /&gt;
   against 0.67-0.86 in production. Supersedes r37433 and r37436 for oppo.&lt;br /&gt;
   calibrate(), refine() and nudgeFor() are now dead and removed.&lt;br /&gt;
&lt;br /&gt;
2. Re-find the slider when it goes stale instead of dropping the imei.&lt;br /&gt;
&lt;br /&gt;
   54 of 284 oppo presses (19%) died on StaleElementReferenceException, against 0&lt;br /&gt;
   of 90 on realme -- the first asymmetry found between the two. Every one aborted&lt;br /&gt;
   the attempt and forced a full page reload.&lt;br /&gt;
&lt;br /&gt;
   The logs place it exactly: &quot;Margin Left before-&quot; succeeds, then the catch fires,&lt;br /&gt;
   so the handle goes stale between that read and moveToElement. It happens on&lt;br /&gt;
   retry iterations (before- 4px, 1px), right after the doubleClick refresh swaps&lt;br /&gt;
   the widget DOM -- the element found at the top of the attempt is already&lt;br /&gt;
   detached by the time we act on it.&lt;br /&gt;
&lt;br /&gt;
   Now re-finds and retries once. The margin-left reads are also wrapped: they are&lt;br /&gt;
   diagnostics and must never be the reason an attempt is abandoned.&lt;br /&gt;
&lt;br /&gt;
Neither change is verified in production yet. The stale fix is the one with a&lt;br /&gt;
measured asymmetry behind it; aim() is a design fix for a mechanism that was&lt;br /&gt;
provably fragile, not a fix for a measured cause of oppo&apos;s 13%.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37437</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37437</guid></item>
<item><pubDate>Thu, 27 Aug 2026 06:20:52 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37436 – Oppo: back to ASSUMED_RATIO 1.7; 0.67 was fitted to contaminated ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo: back to ASSUMED_RATIO 1.7; 0.67 was fitted to contaminated data&lt;br /&gt;
&lt;br /&gt;
r37433 set oppo to 0.67 on the strength of 3,694 production calibrations. It did&lt;br /&gt;
not help: 14% before, 13% after, across two independent windows.&lt;br /&gt;
&lt;br /&gt;
Those 3,694 measurements were all taken while the slider press was leaking&lt;br /&gt;
between attempts (fixed in r37435), so 79% of them started from a corrupted&lt;br /&gt;
pointer state. The value was fitted to noise.&lt;br /&gt;
&lt;br /&gt;
With that confound removed the clean comparison is realme: identical widget,&lt;br /&gt;
identical calibration code, ASSUMED_RATIO 1.7, converting 82% of drags against&lt;br /&gt;
oppo&apos;s 13%. Measured post-fix on comparable gaps:&lt;br /&gt;
&lt;br /&gt;
  Oppo   0.67 -&gt; probe 52px, closes 37% of a 122px gap -&gt; 13% success&lt;br /&gt;
  Realme 1.7  -&gt; probe 31px, closes 11% of a 105px gap -&gt; 82% success&lt;br /&gt;
&lt;br /&gt;
The smaller probe is the one that lands. A probe that eats a third of the gap&lt;br /&gt;
risks crossing the target during calibration, and the gap is measured as&lt;br /&gt;
|c1 - c2|, which cannot distinguish &quot;not there yet&quot; from &quot;gone past&quot; -- so the&lt;br /&gt;
derived ratio comes back wrong and the main move is wrong with it.&lt;br /&gt;
&lt;br /&gt;
This converges on realme&apos;s value but is not a blind sync: it is the one&lt;br /&gt;
configuration observed to work on this widget once the press bug was gone. The&lt;br /&gt;
constants stay per-brand and independently documented.&lt;br /&gt;
&lt;br /&gt;
Not a certainty. r37435 improved realme by 18 points and left oppo flat, so&lt;br /&gt;
something about oppo may still differ; this tests the cheapest remaining&lt;br /&gt;
explanation before going after circle detection.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37436</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37436</guid></item>
<item><pubDate>Thu, 27 Aug 2026 06:11:22 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37435 – Oppo/Realme: always release the slider; 79% of presses were left ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo/Realme: always release the slider; 79% of presses were left held&lt;br /&gt;
&lt;br /&gt;
Found by following the observation that the dragger repositions itself on click.&lt;br /&gt;
&lt;br /&gt;
clickAndHold() is issued at the top of every attempt, but release() sits inside&lt;br /&gt;
the moveByOffset &gt; 0 branch. An attempt that fails circle detection, or produces&lt;br /&gt;
an unusable calibration, therefore leaves the mouse button DOWN.&lt;br /&gt;
&lt;br /&gt;
Measured over 21 minutes of production:&lt;br /&gt;
&lt;br /&gt;
  Oppo    506 presses, 107 released -&gt; 399 never released = 79%&lt;br /&gt;
  Realme  247 presses,  58 released -&gt; 189 never released = 77%&lt;br /&gt;
&lt;br /&gt;
Actions is one instance per driver, created OUTSIDE the imei loop, so the stuck&lt;br /&gt;
press does not end with the attempt -- it leaks into the next attempt and on into&lt;br /&gt;
the next imei. The following clickAndHold then lands on an already-held button,&lt;br /&gt;
so the widget never repositions the handle and the drag simply continues from&lt;br /&gt;
wherever the pointer was abandoned.&lt;br /&gt;
&lt;br /&gt;
That matches what the logs show. Validated locally against both live widgets:&lt;br /&gt;
a clean press repositions the handle +22px on the first pixel of movement (half&lt;br /&gt;
the 45px handle: moveToElement centres the pointer, the widget aligns to it), and&lt;br /&gt;
the transfer is then linear -- 1.10 piece px per mouse px on oppo, 1.20 on realme.&lt;br /&gt;
Production instead shows a jump of +0px on 31 oppo attempts and -34..-41px on&lt;br /&gt;
another 39, and oppo starts 13% of attempts with margin-left already &gt;= 30px&lt;br /&gt;
(mean 9.2px) against realme&apos;s 0% (mean 3.2px). Those are the fingerprints of a&lt;br /&gt;
drag resuming rather than starting.&lt;br /&gt;
&lt;br /&gt;
Tracks the press in a flag and releases on every exit: before the retry refresh,&lt;br /&gt;
and again after the loop so the widget-not-found break cannot carry a held button&lt;br /&gt;
into the next imei. Release is wrapped, so a failure to release is logged rather&lt;br /&gt;
than killing the batch.&lt;br /&gt;
&lt;br /&gt;
Ruled out while getting here, so as not to re-chase: the reposition is NOT an&lt;br /&gt;
animation race (position is stable from t=0 to 1s despite transition:all), and it&lt;br /&gt;
is NOT asymmetric between the brands -- both snap +22px identically, so this bug&lt;br /&gt;
does not by itself explain oppo 14% vs realme 64%. Both brands should improve.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckRealmeWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37435</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37435</guid></item>
<item><pubDate>Thu, 27 Aug 2026 05:33:08 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37434 – Oppo: capture the captcha verify separately from the puzzle refetch ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo: capture the captcha verify separately from the puzzle refetch&lt;br /&gt;
&lt;br /&gt;
Diagnostic only, no behaviour change.&lt;br /&gt;
&lt;br /&gt;
r37433&apos;s verdict logging shows all 58 captured oppo captcha responses are&lt;br /&gt;
/api/a (the puzzle fetch) and none are /api/v1 (the verify), while realme shows&lt;br /&gt;
/api/v1 on 20 of 25 failures. The tempting reading is that oppo&apos;s drag is never&lt;br /&gt;
submitted for verification at all.&lt;br /&gt;
&lt;br /&gt;
That reading is not safe. __oppoCaptcha is last-captcha-call-wins, and the&lt;br /&gt;
widget refetches a puzzle immediately after a rejected verify. Oppo then waits&lt;br /&gt;
up to 5s for the error element before reading the signal -- ample time for the&lt;br /&gt;
refetch to overwrite the verdict. Realme reads off the lookup response instead,&lt;br /&gt;
which is why its verdict survives.&lt;br /&gt;
&lt;br /&gt;
So &quot;never verified&quot; and &quot;verified, rejected, then overwritten&quot; both fit, and&lt;br /&gt;
they need opposite fixes: the first is a broken gesture, the second is aim.&lt;br /&gt;
&lt;br /&gt;
Keeps the last /api/v1 body in its own variable plus a count, so the failure&lt;br /&gt;
line now reads verifies=N lastVerify=... and the two are distinguishable on the&lt;br /&gt;
next tick.&lt;br /&gt;
&lt;br /&gt;
Ruled out already, so as not to re-chase: drag duration. Time held between&lt;br /&gt;
&quot;Distance is&quot; and &quot;Move click performed&quot; is oppo 1.4s mean / 2s max against&lt;br /&gt;
realme 1.6s / 3s -- effectively identical, so the widget is not abandoning&lt;br /&gt;
oppo&apos;s drag for slowness.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37434</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37434</guid></item>
<item><pubDate>Thu, 27 Aug 2026 03:19:34 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37433 – Oppo/Realme: split the slider tuning, and give Oppo its own ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo/Realme: split the slider tuning, and give Oppo its own failure verdict&lt;br /&gt;
&lt;br /&gt;
The two classes drive the same dingxiang widget and have been edited in lockstep,&lt;br /&gt;
but they do not behave alike: measured over the last 11 hours as successes per&lt;br /&gt;
completed drag, oppo runs 11-17% and realme 60%. A change proven on one is not&lt;br /&gt;
evidence for the other, so the tuning constants are now documented as per-brand&lt;br /&gt;
and must not be synced.&lt;br /&gt;
&lt;br /&gt;
Correcting the record while I am here: oppo has NOT regressed. Earlier &quot;43%&quot; and&lt;br /&gt;
&quot;48%&quot; figures were successes over FAILURES, and a small widget-verdict sample --&lt;br /&gt;
not successes over drags. On the consistent denominator oppo has been 12-15%&lt;br /&gt;
throughout, across r37422, r37424 and r37432 alike.&lt;br /&gt;
&lt;br /&gt;
Oppo: ASSUMED_RATIO 1.7 -&gt; 0.67.&lt;br /&gt;
&lt;br /&gt;
  It only sizes the calibration probe: nudge ~= gap / (2 * ratio), intended to&lt;br /&gt;
  close half the gap. 1.7 came from one hand measurement. Production says&lt;br /&gt;
  otherwise -- 3,694 oppo calibrations average 0.67 (realme 0.70 over 1,683), so&lt;br /&gt;
  the probe has been closing about a fifth of the gap, not a half, and the ratio&lt;br /&gt;
  is then derived from that needlessly small and noisy sample. Mean gap is 120px&lt;br /&gt;
  for both, and 28% of oppo calibrations already overshoot.&lt;br /&gt;
&lt;br /&gt;
Oppo: log the captcha widget&apos;s verdict on the failure branch.&lt;br /&gt;
&lt;br /&gt;
  Realme has carried this since r37428 and oppo never did, which is why realme&apos;s&lt;br /&gt;
  failures name POSITION_MISMATCH and oppo&apos;s 85% say nothing. Without it there is&lt;br /&gt;
  no way to tell a bad drag from a widget that never rendered.&lt;br /&gt;
&lt;br /&gt;
Realme: comment only, no behaviour change. Its ratio is wrong in the same way,&lt;br /&gt;
but it is wrong while converting 60%, so oppo carries the corrected value first&lt;br /&gt;
and realme follows only if it helps.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckRealmeWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37433</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37433</guid></item>
<item><pubDate>Thu, 27 Aug 2026 01:38:10 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37432 – Oppo: reinstall the capture hook after every navigation  Oppo ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo: reinstall the capture hook after every navigation&lt;br /&gt;
&lt;br /&gt;
Oppo installed the response hook after the first driver.get only. The other&lt;br /&gt;
three navigations -- the &quot;field already filled&quot; reset, the widget-not-found&lt;br /&gt;
reload, and the per-imei reset at the end of the loop -- did not.&lt;br /&gt;
&lt;br /&gt;
Every navigation destroys the JS context, so __oppoCapInstalled, __oppoResp and&lt;br /&gt;
__oppoCaptcha are all wiped and never recreated. Oppo therefore runs blind from&lt;br /&gt;
the second imei of a batch onwards, permanently.&lt;br /&gt;
&lt;br /&gt;
Live evidence from the 00:49-01:03 window after deploying r37431:&lt;br /&gt;
&lt;br /&gt;
  Success at attempt         12&lt;br /&gt;
  Activation lookup for       1     &amp;lt;- the first imei, right after the initial get&lt;br /&gt;
  No getDeviceInfo response  11&lt;br /&gt;
  captcha said               null on all 11&lt;br /&gt;
&lt;br /&gt;
Both variables null on every miss is the signature: not a failed drag, an&lt;br /&gt;
absent hook. Realme does reinstall at all four of its navigations (r37423) and&lt;br /&gt;
in the same window captured 30 lookups from 30 successes, on the same box with&lt;br /&gt;
the same hook code -- so the hook itself is fine.&lt;br /&gt;
&lt;br /&gt;
Deliberately not touching Oppo&apos;s success signal in this change. It still infers&lt;br /&gt;
success from the bar-inform error element being absent, which is the false&lt;br /&gt;
success pattern r37426 replaced for Realme, and it may well need the same&lt;br /&gt;
treatment. But the null hook explains the misses on its own, and changing both&lt;br /&gt;
at once would make the next measurement unattributable.&lt;br /&gt;
&lt;br /&gt;
Not compile-verified: the local build was interrupted. The change is three calls&lt;br /&gt;
to an existing private method in the same class.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37432</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37432</guid></item>
<item><pubDate>Thu, 27 Aug 2026 00:33:09 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37431 – Realme/Oppo: capture the lookup off Response.prototype, not window.fetch  Realme ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;Realme/Oppo: capture the lookup off Response.prototype, not window.fetch&lt;br /&gt;
&lt;br /&gt;
Realme solves its captcha well -- the widget&apos;s own verify endpoint says&lt;br /&gt;
success:true on 526 of 829 attempts, 63% -- and records nothing. Every one of&lt;br /&gt;
those solved captchas is logged as &quot;Failed&quot; and the imei re-queued.&lt;br /&gt;
&lt;br /&gt;
The lookup is not missing, it is invisible. realme&apos;s client calls Nuxt&apos;s $fetch&lt;br /&gt;
(ofetch), which takes its reference to globalThis.fetch when its bundle is&lt;br /&gt;
evaluated. That happens before driver.get() returns, so by the time&lt;br /&gt;
installResponseCapture wraps window.fetch the client is already holding the&lt;br /&gt;
original. r37425 added that wrapper and could never have worked.&lt;br /&gt;
&lt;br /&gt;
Verified on the live page: the same $fetch request is invisible to a&lt;br /&gt;
window.fetch wrapper and visible to a Response.prototype one.&lt;br /&gt;
&lt;br /&gt;
Response.prototype.json/.text are resolved per call, so patching them catches&lt;br /&gt;
any client whenever it took its fetch reference -- the same reason the&lt;br /&gt;
XMLHttpRequest.prototype.open patch has always worked (it is how the captcha&lt;br /&gt;
verdict gets captured today).&lt;br /&gt;
&lt;br /&gt;
The clone is read with the ORIGINAL reader. Calling the patched one recurses&lt;br /&gt;
forever and hangs the page; that is not theoretical, it hung a test run.&lt;br /&gt;
&lt;br /&gt;
Applied to Oppo as well. Its XHR path works today (673 lookups, 0 missed), so&lt;br /&gt;
this is defence against the same trap following any migration -- which is what&lt;br /&gt;
r37425 was reaching for and did not achieve.&lt;br /&gt;
&lt;br /&gt;
Expected: Realme goes from 0 rows to roughly its captcha rate. Unchanged:&lt;br /&gt;
59% of attempts still never find the circles, same ceiling as Oppo.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckRealmeWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37431</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37431</guid></item>
<item><pubDate>Wed, 26 Aug 2026 20:19:02 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37430 – Realme: widen the lookup match and report what the page ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Realme: widen the lookup match and report what the page actually called&lt;br /&gt;
&lt;br /&gt;
The captcha is being solved -- the widget returns success:true with a token --&lt;br /&gt;
but the lookup response is never captured, so awaitResponse times out and every&lt;br /&gt;
attempt records as failed. 19 &apos;success&apos; against 53 &apos;failed&apos; this hour with 0&lt;br /&gt;
lookups, while the widget&apos;s own verdicts are mostly success:true.&lt;br /&gt;
&lt;br /&gt;
Matches on the customer-api host as well as the active/check path, in case the&lt;br /&gt;
endpoint moved with the page rebuild. A failed attempt now also logs the&lt;br /&gt;
non-captcha URLs the page did call, so if it is still missed the log names what&lt;br /&gt;
to match on rather than leaving it to guesswork.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckRealmeWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37430</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37430</guid></item>
<item><pubDate>Wed, 26 Aug 2026 20:17:48 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37429 – Oppo: wait for the lookup response instead of reading it ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo: wait for the lookup response instead of reading it once&lt;br /&gt;
&lt;br /&gt;
Oppo read window.__oppoResp immediately after the success signal, which races the&lt;br /&gt;
request that produces it: in the 20:00 hour 17 successes yielded 16 &apos;no&lt;br /&gt;
getDeviceInfo response&apos; and a single lookup, against 673 lookups and 0 misses&lt;br /&gt;
earlier in the day.&lt;br /&gt;
&lt;br /&gt;
Now polls for up to 8s, matching Realme (r37426). A failed read also reports the&lt;br /&gt;
captcha widget&apos;s own verdict, so a miss says whether the drag was refused&lt;br /&gt;
(POSITION_MISMATCH) or passed and the lookup simply did not arrive.&lt;br /&gt;
&lt;br /&gt;
Widget verdicts confirm the calibration work: 14 success:true against 18&lt;br /&gt;
POSITION_MISMATCH is 43% real aim accuracy, up from ~9% before r37422.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37429</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37429</guid></item>
<item><pubDate>Wed, 26 Aug 2026 19:59:04 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37428 – Oppo/Realme: record the captcha widget&apos;s own verdict alongside the lookup ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo/Realme: record the captcha widget&apos;s own verdict alongside the lookup&lt;br /&gt;
&lt;br /&gt;
r37426 made the lookup response the success signal, which is authoritative but&lt;br /&gt;
silent about WHY a drag was refused -- a timeout looks identical whether the aim&lt;br /&gt;
was wrong, the widget never rendered, or the page changed again.&lt;br /&gt;
&lt;br /&gt;
The captcha widget answers that itself: it calls captcha-ind-sec.heytapmobile.com&lt;br /&gt;
and its replies carry a status and message. The hook now keeps the last such&lt;br /&gt;
response, and a failed attempt logs it, so &apos;Failed = 3&apos; becomes &apos;Failed = 3&lt;br /&gt;
(captcha said: ...)&apos;.&lt;br /&gt;
&lt;br /&gt;
Note what the widget does NOT give us: its init call returns the puzzle&apos;s y&lt;br /&gt;
coordinate but not the x, which is the secret being protected. So the circle&lt;br /&gt;
detection still has to find the horizontal target -- there is no shortcut there,&lt;br /&gt;
and that remains the largest loss (60% of Oppo attempts never find the circles).&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckRealmeWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37428</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37428</guid></item>
<item><pubDate>Wed, 26 Aug 2026 19:45:06 +0530</pubDate><dc:creator>ranu</dc:creator><title>Rev 37427 – src dashboard logic correction</title><description>&lt;div&gt;&lt;strong&gt;ranu – 3 file(s) modified&lt;/strong&gt;&lt;br/&gt;src dashboard logic correction&lt;/div&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/AnalysisDashboardController.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/DashboardController.java&lt;br /&gt;~ /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/analysisDashboard.vm&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37427</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37427</guid></item>
<item><pubDate>Wed, 26 Aug 2026 19:40:47 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37426 – Realme: treat the lookup response, not a missing error element, ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Realme: treat the lookup response, not a missing error element, as success&lt;br /&gt;
&lt;br /&gt;
After r37425 Realme logged 27 &apos;Success at attempt&apos; and 27 &apos;no active/check&lt;br /&gt;
response captured&apos; in the same window, and wrote 0 rows. The hook was not at&lt;br /&gt;
fault -- reproduced locally, it captures both XHR and fetch correctly.&lt;br /&gt;
&lt;br /&gt;
Success was being inferred from the ABSENCE of dx_captcha_basic_bar-inform:&lt;br /&gt;
&lt;br /&gt;
    try   { wait5Sec.until(visibilityOf(bar-inform)); log(&apos;Failed&apos;) }&lt;br /&gt;
    catch { log(&apos;Success&apos;) }&lt;br /&gt;
&lt;br /&gt;
realme rebuilt this page, so that element may no longer exist. The wait then&lt;br /&gt;
always times out and EVERY attempt reports success while the captcha was never&lt;br /&gt;
solved, no lookup fired and nothing was captured. The 27/27/0 is that exactly.&lt;br /&gt;
&lt;br /&gt;
The response arriving is the only signal that actually proves the captcha&lt;br /&gt;
passed, so it now waits for that (polling, up to 8s) and treats its absence as a&lt;br /&gt;
failure worth retrying. This also removes the separate &apos;no response captured&apos;&lt;br /&gt;
branch, since reaching the success path now means the response is in hand.&lt;br /&gt;
&lt;br /&gt;
Oppo is left alone: its bar-inform detection is working (0 missed captures&lt;br /&gt;
against 673 lookups), so there is nothing to fix there.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckRealmeWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37426</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37426</guid></item>
<item><pubDate>Wed, 26 Aug 2026 19:27:25 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37425 – Oppo/Realme: capture the response from fetch as well as XHR ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo/Realme: capture the response from fetch as well as XHR&lt;br /&gt;
&lt;br /&gt;
r37423 got Realme solving the captcha again -- 4 of 4 on the first tick after&lt;br /&gt;
deploy -- but every one logged &apos;Result shown but no active/check response&lt;br /&gt;
captured&apos;. The capture hook only wrapped XMLHttpRequest, and realme&apos;s page issues&lt;br /&gt;
the lookup through fetch, so it recorded nothing and each imei was stored with a&lt;br /&gt;
null date.&lt;br /&gt;
&lt;br /&gt;
The hook now wraps window.fetch too, cloning the response before reading it so&lt;br /&gt;
the page still consumes its own body. Applied to Oppo as well: its XHR path is&lt;br /&gt;
working today, but the same silent failure would follow any move to fetch, and&lt;br /&gt;
the cost is a few lines.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckRealmeWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37425</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37425</guid></item>
<item><pubDate>Wed, 26 Aug 2026 19:05:20 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37424 – Oppo/Realme slider captcha: size the calibration nudge from the gap ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo/Realme slider captcha: size the calibration nudge from the gap&lt;br /&gt;
&lt;br /&gt;
r37422 replaced a 15px calibration nudge with a fixed 60px, which fixed the&lt;br /&gt;
under-measurement but introduced the opposite error. Production data over 764&lt;br /&gt;
calibrations shows 45% ended UNUSABLE (closed &amp;lt;= 0, i.e. the piece shot past the&lt;br /&gt;
hole and the measured gap grew instead of shrinking), and among gaps under 60px&lt;br /&gt;
it was 85%.&lt;br /&gt;
&lt;br /&gt;
The nudge is now sized from the gap -- aim to close about half of it -- and&lt;br /&gt;
clamped to [10,60]. Replaying the real observed gap distribution: unusable and&lt;br /&gt;
overshoot fall from 21% to 2%.&lt;br /&gt;
&lt;br /&gt;
The observed travel ratio in production is 1.13 circle px per slider px, against&lt;br /&gt;
1.68 measured locally; it varies with render scale, which is why the ratio is&lt;br /&gt;
still derived from the measurement. ASSUMED_RATIO only sizes the probe, and 1.7&lt;br /&gt;
errs toward a smaller nudge, which is the safe direction here.&lt;br /&gt;
&lt;br /&gt;
Oppo is currently solving 43% (was 9% before r37422). Realme carries the same&lt;br /&gt;
widget and the same defect and has not been deployed yet.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckRealmeWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37424</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37424</guid></item>
<item><pubDate>Wed, 26 Aug 2026 18:59:28 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37423 – Realme warranty check: retarget the rebuilt page and read its ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Realme warranty check: retarget the rebuilt page and read its JSON&lt;br /&gt;
&lt;br /&gt;
Realme has recorded nothing since 24-Aug, the same day Oppo stopped. Driving the&lt;br /&gt;
page confirms why: it was rebuilt and every selector the code used is gone.&lt;br /&gt;
&lt;br /&gt;
  .sn-input      -&gt; the field is now .imei-input input (placeholder &apos;Please input IMEI&apos;)&lt;br /&gt;
  div.check-btn  -&gt; the submit is now an icon, .imei-input .search-icon&lt;br /&gt;
&lt;br /&gt;
So it failed even earlier than Oppo did -- it could not type the imei, let alone&lt;br /&gt;
reach the captcha, which is why the log showed only &apos;Initiating webdriver...&apos;.&lt;br /&gt;
&lt;br /&gt;
The result now comes from the JSON the page already receives&lt;br /&gt;
(customer-api.realme.com/in/active/check) rather than an XPath keyed off a label&lt;br /&gt;
that no longer exists. That old locator also had an &apos;| //h1[contains(@class,&lt;br /&gt;
title)]&apos; branch which would silently match a page heading and try to parse it as&lt;br /&gt;
a date. The API is explicit: expiryDate (yyyy.MM.dd) and an isActivation flag,&lt;br /&gt;
so nothing is inferred from message text.&lt;br /&gt;
&lt;br /&gt;
expiryDate is the WARRANTY EXPIRY, so a year is still subtracted -- warranty is&lt;br /&gt;
12 months as standard. Checked against our own records rather than assumed:&lt;br /&gt;
41,267 of 41,289 stored Realme activation dates precede the date we checked&lt;br /&gt;
them, averaging 13 days before, which is only consistent with the subtraction&lt;br /&gt;
already being correct.&lt;br /&gt;
&lt;br /&gt;
Ports the r37422 captcha fix verbatim -- identical dingxiang slider, identical&lt;br /&gt;
15px calibration nudge that made it land ~30px out against a ~30px hole. And as&lt;br /&gt;
with Oppo the outcome is always recorded, even as null: findElement THREW when&lt;br /&gt;
the element was missing, so nothing was written and the imei was re-queued every&lt;br /&gt;
5 minutes indefinitely.&lt;br /&gt;
&lt;br /&gt;
Also converts 29 System.out.println to the logger. The cron JVM&apos;s stdout is a&lt;br /&gt;
socket from the launching ssh session, so every diagnostic was going nowhere.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckRealmeWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37423</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37423</guid></item>
<item><pubDate>Wed, 26 Aug 2026 17:11:44 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37422 – Oppo slider captcha: calibrate over a longer nudge and correct ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo slider captcha: calibrate over a longer nudge and correct before releasing&lt;br /&gt;
&lt;br /&gt;
The captcha solves about 10 times in 115 attempts. The arithmetic was right; the&lt;br /&gt;
measurement feeding it was not.&lt;br /&gt;
&lt;br /&gt;
To place the piece the code must first learn how far it travels per slider pixel,&lt;br /&gt;
which it did by nudging 15px and re-measuring. The piece moves ~1.7px per slider&lt;br /&gt;
px, so 15px closed the gap by only ~25px -- and Hough circle detection carries&lt;br /&gt;
about +/-2-3px per circle, so the ratio came from a measurement roughly 20%&lt;br /&gt;
wrong. That error was then multiplied across the whole remaining travel: on a&lt;br /&gt;
150px gap it landed ~30px out, against a hole ~30px wide.&lt;br /&gt;
&lt;br /&gt;
Nudging 60px closes ~100px, so the same detection noise is ~5% rather than ~20%.&lt;br /&gt;
The code also released on its single computed offset, committing all of that&lt;br /&gt;
error to the final position; it now measures once more after the main move and&lt;br /&gt;
closes the remainder before releasing. Math.round replaces an (int) cast that&lt;br /&gt;
biased every move short by up to a pixel.&lt;br /&gt;
&lt;br /&gt;
Monte Carlo over the measurement noise (20k trials, +/-8px tolerance): median&lt;br /&gt;
placement error 16.3px -&gt; 2.1px, success 26% -&gt; 98%. Holds across a 2x range of&lt;br /&gt;
travel ratios and degrades gracefully as detection worsens, because the ratio is&lt;br /&gt;
derived rather than assumed. Real-world will be lower -- the same model puts the&lt;br /&gt;
old code at 26% where production sees 9% -- so expect roughly 1 in 3, not 49 in&lt;br /&gt;
50, and read Success/Failed in the log to get the true figure.&lt;br /&gt;
&lt;br /&gt;
The ratio is returned rather than held on a field: this is a singleton and the&lt;br /&gt;
Oppo secondary and tertiary jobs run through it concurrently.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37422</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37422</guid></item>
<item><pubDate>Wed, 26 Aug 2026 16:47:32 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37421 – EWB: recover existing e-way bill on NIC 604 instead of ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 2 file(s) modified&lt;/strong&gt;&lt;br/&gt;EWB: recover existing e-way bill on NIC 604 instead of storing a placeholder&lt;br /&gt;
&lt;br /&gt;
DCNSUPDL948 generated EWB 451765074092 at NIC, then the transaction rolled back and&lt;br /&gt;
the number was lost. Every retry hit 604 and stored the literal EXISTING-LOOKUP-NEEDED&lt;br /&gt;
with no validity date, which routed the PDF down the transporter branch and NPE&apos;d on a&lt;br /&gt;
self-pickup dispatch with no warehouse_provider row. ~640 retries in 55 minutes, and&lt;br /&gt;
because the failure surfaced as an Error it escaped catch(Exception) and blocked six&lt;br /&gt;
other documents behind it.&lt;br /&gt;
&lt;br /&gt;
- GstProService: on 604, look the bill up via GetEwayBillsByDate + docNo match and&lt;br /&gt;
  return it in GENEWAYBILL shape; stamp NIC&apos;s generation time instead of now().&lt;br /&gt;
  Throw when it cannot be recovered rather than persist a placeholder.&lt;br /&gt;
- InvoiceService: saveInvoiceInNewTransaction no longer propagates. It commits&lt;br /&gt;
  irn_generated=0 with the reason so a failed document stops churning; already-filed&lt;br /&gt;
  invoices being re-rendered are left untouched.&lt;br /&gt;
- InvoiceService: cron loop catches Throwable so one bad document cannot skip the batch.&lt;br /&gt;
- InvoiceService: null-guard warehouse_provider; omit the transporter line instead of&lt;br /&gt;
  failing the PDF.&lt;br /&gt;
- recordIrnFailure: transport failures now park at 0 for escalation rather than&lt;br /&gt;
  requeueing for unbounded retry.&lt;/div&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/GstProService.java&lt;br /&gt;~ /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/transaction/invoicing/InvoiceService.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37421</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37421</guid></item>
<item><pubDate>Wed, 26 Aug 2026 16:23:17 +0530</pubDate><dc:creator>amit</dc:creator><title>Rev 37420 – Oppo warranty check: read the JSON response instead of scraping ...</title><description>&lt;div&gt;&lt;strong&gt;amit – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Oppo warranty check: read the JSON response instead of scraping the date&lt;br /&gt;
&lt;br /&gt;
Oppo has recorded no activation dates since 23-Aug. With logging restored&lt;br /&gt;
(r37410) the cause is visible: the captcha IS solved -- 10 successes per tick --&lt;br /&gt;
and it then fails on the very next line with&lt;br /&gt;
&lt;br /&gt;
  no such element: //*[contains(text(),&apos;UTC+5.5&apos;) or contains(text(),&apos;Non-Activate&apos;)]&lt;br /&gt;
&lt;br /&gt;
Their result is now label/value pairs, and the value is rendered from TWO text&lt;br /&gt;
nodes: dayjs(regDate).format(&apos;DD/MM/YYYY&apos;) plus a &apos;(UTC+x)&apos; suffix computed from&lt;br /&gt;
the browser&apos;s own timezone. XPath contains(text(),...) tests only the first node&lt;br /&gt;
-- the date -- so it never matched. &apos;Non-Activate&apos; is gone too: an inactive&lt;br /&gt;
device now reads &apos;The system will update the date within 7 days of device&lt;br /&gt;
activation.&apos; Verified against a replica of their DOM: old locator 0 matches, and&lt;br /&gt;
the value element yields &apos;23/08/2026 (UTC+5.5)&apos;.&lt;br /&gt;
&lt;br /&gt;
Rather than chase their markup again, take the value from the source: the page&lt;br /&gt;
calls /oppo-api/basic/v1/getDeviceInfo and the response carries regDate as epoch&lt;br /&gt;
millis -- no date format, no locale, no timezone suffix to parse. A small hook&lt;br /&gt;
records that response as the page receives it; nothing extra is requested and no&lt;br /&gt;
captcha behaviour changes.&lt;br /&gt;
&lt;br /&gt;
Also switches the outcome to always record the imei. findElement THREW when the&lt;br /&gt;
element was missing, which skipped the fallback entirely, so nothing was written&lt;br /&gt;
to dateMap and the row was re-queued on every run indefinitely -- &apos;Could not&lt;br /&gt;
capture date&apos; appears 0 times in the log because it was unreachable.&lt;/div&gt;~ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/CheckOppoWarrantyTask.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37420</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37420</guid></item>
<item><pubDate>Wed, 26 Aug 2026 14:03:37 +0530</pubDate><dc:creator>vikas</dc:creator><title>Rev 37419 – Whatsapp botpenguin</title><description>&lt;div&gt;&lt;strong&gt;vikas – 1 file(s) modified&lt;/strong&gt;&lt;br/&gt;Whatsapp botpenguin&lt;/div&gt;+ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/remarkscore/RemarkScoreService.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37419</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37419</guid></item>
<item><pubDate>Wed, 26 Aug 2026 14:02:05 +0530</pubDate><dc:creator>vikas</dc:creator><title>Rev 37418 – Whatsapp botpenguin</title><description>&lt;div&gt;&lt;strong&gt;vikas – 3 file(s) modified&lt;/strong&gt;&lt;br/&gt;Whatsapp botpenguin&lt;/div&gt;+ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/remarkscore&lt;br /&gt;+ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/remarkscore/DuplicateRemarkDigestRunner.java&lt;br /&gt;+ /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/remarkscore/RemarkScoreRunner.java&lt;br /&gt;</description><link>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37418</link><guid>https://svn.smartdukaan.com/revision.php?repname=SmartDukaan&amp;path=%2F&amp;isdir=1&amp;rev=37418</guid></item>
</channel></rss>