Major rewrite:
- Queue now processes batches: addToCart×N → saveMetadata×N → ONE
submitOrder → poll → download ALL documents → store in MinIO
- Removed unique constraint on orderId (shared across batch items)
- Added step=download to test endpoint: downloads PDFs from 5
existing orders (9685480-9685484) and stores in MinIO
- step=order now uses enqueueBatch for 2 test parcels (61904, 309952)
as ONE ePay order instead of separate orders
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EditCartSubmit returns 200 (not redirect) — Angular does client-side
redirect to CheckoutConfirmationSubmit.action. Added this step to
actually confirm the order before looking for the orderId.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
submitOrder now captures the previous orderId BEFORE submitting, then
searches for a NEW orderId that isn't in the knownOrderIds set. Queue
passes knownOrderIds between sequential items to prevent duplicate
orderId assignment (unique constraint violation).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Angular uses doPostAsFormMultipart — the save endpoint requires
multipart/form-data, not application/x-www-form-urlencoded.
Install form-data package and restore multipart upload.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
form-data package not installed — crashes at runtime. Use
URLSearchParams instead (the Angular source uses doPostAsForm
which is form-urlencoded, so this should work too).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Zero discovery calls needed! ePay internal county IDs are identical
to eTerra WORKSPACE_IDs (CLUJ=127, ALBA=10, etc.) and ePay UAT IDs
are SIRUTA codes (Cluj-Napoca=54975, Florești=57706). Queue now
uses workspacePk + siruta directly from GisUat DB.
Flow: AddToCart → saveMetadata → EditCartSubmit → Poll+Download.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SearchEstate might need active cart and/or different headers.
Add X-Requested-With: XMLHttpRequest, make uatId optional, log raw
response (type, length, sample), and add-to-cart before searching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OpenAM requires an initial GET to set session cookies before the
credentials POST. Without it, POST returns 500 and only sets
AMAuthCookie (intermediate) instead of iPlanetDirectoryPro (final SSO).
Then navigate to ePay goto URL to establish JSESSIONID.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OpenAM goto URL is http://epay.ancpi.ro:80 (HTTP, not HTTPS).
Try multiple URL variants to establish JSESSIONID after OpenAM auth.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ANCPI's OpenAM uses AMAuthCookie instead of iPlanetDirectoryPro.
Accept AMAuthCookie, iPlanetDirectoryPro, or JSESSIONID as valid
session indicators. Navigate to ePay after auth to establish JSESSIONID.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Let axios follow all redirects (maxRedirects=10) so cookie jar captures
iPlanetDirectoryPro from the chain. Explicitly navigate to ePay after
login to ensure JSESSIONID. Log all cookies for debugging. Last resort:
verify login by checking if credit info is visible on ePay page.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OpenAM requires module=SelfRegistration and goto= redirect URL.
Also handle 302 manually to capture iPlanetDirectoryPro cookie,
then follow redirect to ePay for JSESSIONID.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Foundation (Phase 1):
- CfExtract Prisma model with version tracking, expiry, MinIO path
- epay-types.ts: all ePay API response types
- epay-counties.ts: WORKSPACE_ID → ePay county index mapping (42 counties)
- epay-storage.ts: MinIO helpers (bucket, naming, upload, download)
- docker-compose.yml: ANCPI env vars
ePay Client (Phase 2):
- epay-client.ts: full HTTP client (login, credits, cart, search estate,
submit order, poll status, download PDF) with cookie jar + auto-relogin
- epay-session-store.ts: separate session from eTerra
Queue + API (Phase 3):
- epay-queue.ts: sequential FIFO queue (global cart constraint),
10-step workflow per order with DB status updates at each step
- POST /api/ancpi/session: connect/disconnect
- POST /api/ancpi/order: create single or bulk orders
- GET /api/ancpi/orders: list all extracts
- GET /api/ancpi/credits: live credit balance
- GET /api/ancpi/download: stream PDF from MinIO
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: PATCH endpoint created a new EterraClient which tried
to re-login with expired session → 401. Now county refresh runs
immediately after successful login in the session route, using the
same authenticated client (fire-and-forget). Component reloads UAT
data 5s after connection to pick up fresh county info.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Restore SIRUTA code display in parentheses next to UAT name
- PATCH response now includes debug samples (sampleUat keys, county
raw data) visible in browser console for diagnosing matching issues
- POST endpoint now supports resync (upsert mode, safe to call again)
- Client logs full PATCH result to browser console for debugging
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- PATCH /api/eterra/uats: handle nested responses (unwrapArray), try
multiple field names (extractName/extractCode), log sample UAT for
debugging, match by code first then by name
- GET /api/eterra/uats: include localFeatures count per SIRUTA via
GisFeature groupBy query
- Dropdown: show green badge with local feature count, county with dash
- Add SKILLS.md for ParcelSync/eTerra/GIS module context
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PATCH /api/eterra/uats fetches counties from eTerra nomenclature and
LIMITE_UAT layer, then batch-updates GisUat records with county name
and workspacePk. Auto-triggers on first eTerra connection when county
data is missing. Helps distinguish same-name UATs in different counties.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix display format: institutions without a person name no longer show
as "(Company)" — now shows just "Company"
- Three-case formatting: name+company → "Name (Company)", only company
→ "Company", only name → "Name"
- Registry table now resolves sender/recipient live from address book
via contactMap — edits in address book reflect immediately in registry
- New contacts created via quick-contact are added to contactMap on the fly
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- QuickContactDialog now has Company/Organization field
- Either name or company is required (same logic as address book)
- Auto-sets type to "institution" when only company is provided
- Display name in registry form uses company as fallback
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Either name or company/organization is now required (not just name)
- When only company is set, it shows as primary display name
- Added department field to ContactPerson sub-entities
- Department shown as badge in card and detail views
- Updated vCard export to handle nameless contacts and department field
- Sort contacts by name or company (whichever is set)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ghostscript -sDEVICE=pdfwrite fundamentally re-encodes fonts, causing
garbled text regardless of parameters. This cannot be fixed.
New approach:
- Local: qpdf-only lossless structural optimization (5-30% savings,
zero corruption risk — fonts and images completely untouched)
- Cloud: iLovePDF API integration (auth → start → upload → process →
download) with 3 levels (recommended/extreme/low), proper image
recompression without font corruption
Frontend: 3 modes (cloud recommended, cloud extreme, local lossless).
Docker: ILOVEPDF_PUBLIC_KEY env var added.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The -dPDFSETTINGS=/screen GS preset overwrites font encoding tables,
producing garbled text in output PDFs. Replace with individual params
that ONLY compress images while preserving fonts intact.
Three quality levels via GS (no Stirling dependency):
- extreme: 100 DPI, QFactor 1.2 (~quality 35)
- high: 150 DPI, QFactor 0.76 (~quality 50)
- balanced: 200 DPI, QFactor 0.4 (~quality 70)
Route all UI modes through the GS endpoint with level parameter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Status "inchis" means the correspondence is resolved, not that the
document validity stopped mattering. Remove status filters from both
ImminentActions dashboard and detail panel Remindere section.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Strip institutional legal deadlines from the dashboard — show only documents
expiring and AC validity within 60-day horizon. Rename to "De reînnoit".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add "Remindere & Alerte" section in entry detail panel showing AC validity,
expiry date, and tracked deadline status with color-coded indicators
- Add quick expiry date buttons (6/12/24 months from document date) in entry form
- Default dosare view to show only active threads
- Add ImminentActions dashboard component showing urgent items (expired docs,
AC validity warnings, overdue/imminent deadlines) sorted by urgency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Scale calculator now supports all 4 real-world units:
- mm, cm, m, km — toggle buttons next to mode selector
- Formula adapts via unit→mm multiplier (mm=1, cm=10, m=1000, km=1M)
- Real→Desen: input in chosen unit, output always mm on drawing
- Desen→Real: output in chosen unit, secondary line shows all other units
- Switching unit clears input to avoid confusion
- step attribute adapts per unit (km=0.001, m=0.01, others=0.5)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Scale calculator:
- Real→Desen: val(m) × 1000 ÷ scale = mm pe desen (ex: 5m la 1:100 → 50mm)
- Desen→Real: val(mm) × scale ÷ 1000 = m real (ex: 50mm la 1:100 → 5m)
- Previously both formulas were wrong (missing ×1000/÷1000 unit conversion)
Remove RoomAreaCalculator (not useful) and its tab/imports
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add ACValidityPeriod type (12 | 24) and validityMonths field to ACValidityTracking
- Replace hardcoded 12-month validity with configurable dropdown (12/24 luni)
- Update computed dates, reminder counter, and tooltip to use selected period
- Add "Configurare remindere si alerte" section in Ghid termene with:
- Threshold table (urgent 5z, depasit 0z, CU alert 30z, AC monthly, prelungire 45z lucr, anuntare 10z cal, transmitere 1z)
- Pause/resume explanation for clarification requests
- Update AC expiry description to mention configurable 12/24 month validity
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- DeadlineTimeline gains onResolveInline callback prop
- Milestone labels show small green checkmark button for resolvable items
- Clicking opens inline text input (motiv + OK/Cancel, Enter/Escape)
- RegistryEntryDetail wires resolve via onResolveDeadline prop
- Milestone date labels show "Data maximă: ..." on hover
- Auto-refreshes viewed entry after inline resolve
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- aggregateDeadlines() now skips entries with status "inchis"
- Auto-tracked/background deadlines excluded from active/urgent/overdue counts
- Only user-created deadlines affect badge numbers
- Milestone dots vertically centered on progress bar (top-[5px], h-2.5)
- Milestone tooltips now show full date ("Data maximă: 15 februarie 2026")
- Countdown text shows date on hover too
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- TrackedDeadline gains pausedAt + totalPausedDays fields
- pauseDeadline() / resumeDeadline() in deadline-service with audit log
- Auto-pause: when incoming conex linked to parent with active deadlines
- Auto-resume: when outgoing conex linked to parent with paused deadlines
(shifts dueDate forward by paused days)
- Timeline shows "Suspendat" state with blue pulsing progress bar
- Milestone tooltips now show exact dates (hover: "Data maximă: ...")
- ISC warning text on expired emission deadlines
- Verification expired text matches PDF spec
- DeadlineAuditEntry gains "paused" | "resumed" action types
- getDeadlineDisplayStatus returns "Suspendat" (blue) for paused deadlines
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- groupDeadlinesByEntry skips entries with status "inchis"
- closeEntry auto-resolves all pending deadlines on close (main + linked)
- Fixes S-2026-00001 showing as overdue despite being closed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move threadChain useMemo before the `if (!entry) return null` early
return to keep hook call order stable between renders. When entry was
null, the hook was skipped, causing "Rendered more hooks than during
the previous render" crash on subsequent renders with entry set.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Progress bar now shows auto-tracked sub-deadline milestones as dots
- Passed milestones: grayed out dot + strikethrough label
- Active milestones: amber dot with descriptive label
- Each milestone has a tooltip with context-aware text (e.g.,
"Verificare expirata — nu se mai pot solicita clarificari")
- Legend below progress bar shows all milestone labels
- Wider progress bar (w-32) to accommodate milestone dots
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New DeadlineConfigOverview component: read-only reference page showing all
18 deadline types organized by category with chain flow diagrams
- Accessible via "Ghid termene" toggle in the "Termene legale" tab
- Shows: summary stats, color legend, collapsible category sections,
chain flow diagrams (fan-in + sequential), notification overview,
document expiry settings, and legal reference index
- All data derived from existing catalog (zero API calls)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Entry created via "Inchide" flow now gets status "inchis" with closureInfo
- New DeadlineTimeline component: main deadlines as cards with progress bar,
auto-tracked sub-deadlines as milestone dots on horizontal timeline
- Auto-tracked deadlines hidden from dashboard when user deadlines exist
- Verification milestone shows "Expirat — nu se mai pot solicita clarificari"
- Parent closureInfo now includes linkedEntryId/Number of the closing act
- Removed orphaned deadline-table.tsx and use-deadline-filters.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Major UX overhaul of the "Termene legale" and thread tabs:
Deadline dashboard:
- Replace 6 KPI cards with simple summary bar (active/urgent/overdue)
- Replace flat table with grouped list by entry (cards with progress bars)
- Chain deadlines collapsed by default with expand toggle
- Auto-tracked/background deadlines hidden from main list
Flow diagram (new component):
- CSS-only horizontal flow diagram showing document chains
- Nodes with direction bar (blue=intrat, orange=iesit), number, subject, status
- Solid arrows for thread links, dashed for conex/linked entries
- Used in both "Dosare" tab (full) and detail panel (compact, max 5 nodes)
Thread explorer → Dosare:
- Renamed tab "Fire conversatie" → "Dosare"
- Each thread shown as a card with flow diagram inside
- Simplified stats (just active/finalized count)
Background tracking:
- comunicare-aviz-beneficiar marked as backgroundOnly (not shown in dashboard)
- Transmission status computed and shown in detail panel (on-time/late)
Auto-resolution:
- When closing entry via reply, matching parent deadlines auto-resolve
- Resolution note includes the reply entry number
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Only the last entry in a company+year sequence can be deleted. Trying
to delete an earlier number (e.g. #2 when #3 exists) returns a 409
error with a Romanian message explaining the restriction.
Also routes UI deletes through the API (like create/update) so they
get proper audit logging and sequence recalculation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PostgreSQL JSONB value::text serializes JSON with spaces after colons
("number": "B-2026-00001") but all LIKE patterns searched for the
no-space format ("number":"B-2026-00001"), causing zero matches and
every new entry getting sequence #1.
Fixed in allocateSequenceNumber, recalculateSequence, and debug-sequences.
Added PATCH handler to migrate old-format entries (BTG/SDT/USW/GRP)
to new single-letter format (B/S/U/G).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PostgreSQL POSIX regex on the server does not support \d shorthand,
causing SUBSTRING to return NULL and every entry to get sequence 1.
Replaced all \d with [0-9] in:
- allocateSequenceNumber (new + old format queries)
- recalculateSequence (new + old format queries)
- debug-sequences endpoint (GET + POST queries)
Also added samples field to debug GET for raw number diagnostics,
and POST now handles old-format entries (BTG→B mapping) with
ON CONFLICT GREATEST for proper counter merging.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>