mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Invoices are produced by the freight workflow rather than by a person, so the production path for filing is a sweep, not the manual endpoint. A @Cron picks the oldest never-submitted invoice and hands it to the existing EimsInvoiceRegistrationService -- no registration logic is duplicated, and the durable reservation still decides whether the submission may proceed. Sweeping rather than hooking the eleven places an invoice can be created or issued keeps the workflow untouched, puts the HTTP call outside the invoice transaction by construction, and lets a crash or restart be picked up on the next tick. invoices.eims_status is the queue; nothing new is persisted. Only NOT_SUBMITTED is eligible: UNKNOWN is never retried automatically because the document may already be filed, and FAILED waits for an explicit retry policy. The tick also refuses to start while eims_system_state holds an in-flight submission or a block, and only one invoice is filed per tick so a misconfiguration costs one rejected document rather than a burst. Requires both EIMS_ENABLED and EIMS_AUTO_SUBMIT; the second defaults to false so authentication can be live long before filing is. Logs carry the invoice number, status and IRN only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
191 lines
7.8 KiB
Plaintext
191 lines
7.8 KiB
Plaintext
# Copy to .env for local/docker compose (not committed).
|
|
PORT=3001
|
|
# @tria-plc/auditlog's client interceptor stamps every AuditLog row's
|
|
# `application` from this env var directly, bypassing MezgebModule.forRoot's
|
|
# applicationName option (package quirk). audit.controller.ts reads the same
|
|
# var when filtering reads, so this can be anything as long as it's set.
|
|
APPLICATION_NAME=freight-api
|
|
# Also required for @tria-plc/auditlog: its producer (AuditClientModule)
|
|
# reads the RMQ URL at package IMPORT time, before MezgebModule.forRoot's
|
|
# rmqUrl option ever runs, so only an env var reaches it — an in-code
|
|
# override is too late. Without this, audit events are silently dropped
|
|
# (no error, nothing published). Point it at whatever broker/vhost your
|
|
# RabbitMQ actually has a user provisioned on.
|
|
RABBITMQ_URL=amqp://localhost:5672
|
|
# GT06 GPS tracker TCP listener port (raw TCP, must be reachable by tracker SIMs). 0 disables.
|
|
GT06_TCP_PORT=5023
|
|
DB_HOST=localhost
|
|
DB_PORT=5433
|
|
DB_USER=postgres
|
|
DB_PASSWORD=
|
|
DB_NAME=edr_freight
|
|
|
|
# Telebirr payment gateway (freight merchant credentials)
|
|
TELEBIRR_BASE_URL=
|
|
TELEBIRR_WEB_BASE_URL=
|
|
TELEBIRR_FABRIC_APP_ID=
|
|
TELEBIRR_APP_SECRET=
|
|
TELEBIRR_MERCHANT_APP_ID=
|
|
TELEBIRR_MERCHANT_CODE=
|
|
TELEBIRR_NOTIFY_URL=https://freight-api.edr.et/payments/webhooks/telebirr
|
|
TELEBIRR_RETURN_URL=
|
|
TELEBIRR_TIMEOUT_EXPRESS=15m
|
|
TELEBIRR_PRIVATE_KEY=
|
|
TELEBIRR_PUBLIC_KEY=
|
|
TELEBIRR_INSECURE_TLS=false
|
|
|
|
# Public origin of the freight customer portal. Password-reset links sent to
|
|
# customers are built against this — it must be browser-reachable.
|
|
FREIGHT_PORTAL_URL=http://localhost:5173
|
|
|
|
# Portal pages the payment provider redirects the browser to after payment.
|
|
# Point these at the freight portal's public payment result routes.
|
|
PAYMENT_RETURN_URL=http://localhost:5173/payment/success
|
|
PAYMENT_FAILURE_URL=http://localhost:5173/payment/failure
|
|
|
|
# Drain tail (minutes) added to every booking pay window before anything expires:
|
|
# settlement is asynchronous, so a payment made in the window's last seconds lands
|
|
# after the deadline. Nothing is expired, no wagons are resold and no window cycle
|
|
# concludes until the tail passes. Defaults to 5 when unset.
|
|
FREIGHT_PAYMENT_DRAIN_MINUTES=5
|
|
# JWT (used by @tria-plc/api-common SharedAuthModule)
|
|
JWT_SECRET=
|
|
JWT_ACCESS_TOKEN_SECRET=
|
|
JWT_REFRESH_TOKEN_SECRET=
|
|
JWT_EXPIRES_IN=3600
|
|
# JWT expiry for @tria-plc/api-common token utils (jsonwebtoken timespan format)
|
|
JWT_ACCESS_TOKEN_EXPIRES=1h
|
|
JWT_REFRESH_TOKEN_EXPIRES=7d
|
|
|
|
# IAM seed defaults (used by @tria-plc/iamapi-common on first boot)
|
|
SUPER_ADMIN_EMAIL=superadmin@tria.com
|
|
SUPER_ADMIN_PHONE=
|
|
# Super-admin password. Falls back to DEFAULT_PASSWORD when empty.
|
|
SUPER_ADMIN_DEFAULT_PASSWORD=
|
|
DEFAULT_PASSWORD=password@tria
|
|
|
|
# IAM baseline shared with edr-passenger-api (roles, IAM app + permissions,
|
|
# position types, organization types + default units, org/unit settings, super
|
|
# admin). Replaces the seeder that shipped inside @tria-plc/iamapi-common — see
|
|
# packages/iam-seed. Seeds by DEFAULT when unset; every write is insert-only.
|
|
# Set to false to opt out.
|
|
SEED_IAM_BASELINE=true
|
|
|
|
# Freight org + staff (bookings / rule-engine IAM)
|
|
SEED_EDR_ORG=true
|
|
SEED_FREIGHT_STAFF=true
|
|
SEED_EXPORT_DJIBOUTI_INTERCHANGE_DEMO=false
|
|
|
|
# MinIO (used by @tria-plc/iamapi-common for file storage)
|
|
MINIO_ENDPOINT=localhost
|
|
MINIO_PORT=9000
|
|
MINIO_USE_SSL=false
|
|
MINIO_ACCESS_KEY=
|
|
MINIO_SECRET_KEY=
|
|
# Preset region so signed URLs are generated locally (no GetBucketLocation
|
|
# network call per sign). MinIO's default is us-east-1.
|
|
MINIO_REGION=us-east-1
|
|
|
|
# Redis
|
|
REDIS_HOST=localhost
|
|
REDIS_PORT=6379
|
|
|
|
# --- Notification broker (RabbitMQ) ---------------------------------------------
|
|
# SMS/email OTP + notifications are queued to RabbitMQ (consumed by the shared
|
|
# SMS/email services). Set RABBITMQ_ENABLED=false to skip the broker entirely
|
|
# (dev without a local broker).
|
|
RABBITMQ_ENABLED=false
|
|
RABBITMQ_URL=amqp://localhost:5672
|
|
SMS_QUEUE=sms_queue
|
|
|
|
# ── VeriFayda 2.0 (eSignet OIDC) identity verification ──────────────────────
|
|
# Disabled by default; /fayda/verification/start returns 503 until enabled.
|
|
FAYDA_ENABLED=false
|
|
FAYDA_CLIENT_ID=
|
|
FAYDA_AUTHORIZATION_ENDPOINT=
|
|
FAYDA_TOKEN_ENDPOINT=
|
|
FAYDA_USERINFO_ENDPOINT=
|
|
# Base64-encoded RSA private JWK used for the private_key_jwt client assertion
|
|
FAYDA_PRIVATE_KEY_BASE64=
|
|
# OAuth redirect_uri for MOBILE clients (must be registered with eSignet)
|
|
FAYDA_REDIRECT_URI=http://localhost:3001/api/fayda/verification/complete
|
|
# OAuth redirect_uri for WEB clients. Defaults to FAYDA_REDIRECT_URI when unset.
|
|
FAYDA_WEB_REDIRECT_URI=http://localhost:3000/fayda/callback
|
|
# OAuth redirect_uri for the customer portal (its own origin — must also be
|
|
# registered with eSignet). Defaults to FAYDA_WEB_REDIRECT_URI when unset.
|
|
FAYDA_PORTAL_REDIRECT_URI=http://localhost:5173/fayda/callback
|
|
CLIENT_ASSERTION_TYPE=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
|
|
FAYDA_SCOPE=openid profile email phone address
|
|
FAYDA_ACR_VALUES=mosip:idp:acr:generated-code
|
|
FAYDA_CLAIMS_LOCALES=en am
|
|
FAYDA_SESSION_TTL_MINUTES=10
|
|
EXPIRATION_TIME=15
|
|
ALGORITHM=RS256
|
|
EMAIL_QUEUE=email_queue
|
|
|
|
# Shared secret for service-to-service calls (payment microservice <-> freight).
|
|
# Required at boot; set ALLOW_UNAUTH_INTERNAL=true instead ONLY for local dev.
|
|
SERVICE_AUTH_TOKEN=change-me
|
|
|
|
# ── MoR EIMS e-invoicing (core.mor.gov.et) ─────────────────────────────────
|
|
# Disabled by default; every EIMS call fails fast with EIMS_NOT_CONFIGURED until enabled.
|
|
EIMS_ENABLED=false
|
|
EIMS_BASE_URL=https://core.mor.gov.et
|
|
EIMS_CLIENT_ID=
|
|
EIMS_CLIENT_SECRET=
|
|
EIMS_API_KEY=
|
|
EIMS_TIN=
|
|
# Source-system identity comes from the access token's systemNumber/systemType claims.
|
|
# Setting these turns them into expected-value checks: a mismatch against the token fails
|
|
# fast rather than one side silently winning. Leave empty to take the gateway's word.
|
|
EIMS_SYSTEM_NUMBER=
|
|
EIMS_SYSTEM_TYPE=
|
|
# Absolute paths to the INSA-issued credentials. Keep them OUTSIDE the repo; the file
|
|
# patterns are gitignored, but a path outside the working tree is safer still.
|
|
# The certificate is transmitted as base64 of this file's exact bytes — do not convert it.
|
|
EIMS_PRIVATE_KEY_PATH=
|
|
EIMS_CERTIFICATE_PATH=
|
|
# Optional tuning
|
|
EIMS_HTTP_TIMEOUT_MS=30000
|
|
EIMS_TOKEN_SKEW_SECONDS=45
|
|
|
|
# ── EIMS invoice registration (required only to register invoices) ─────────
|
|
# Seller identity: EDR's own legal details are not modelled anywhere in the DB.
|
|
# Region and Wereda are MoR *codes* (e.g. 13 / 574), not names.
|
|
EIMS_SELLER_LEGAL_NAME=
|
|
EIMS_SELLER_VAT_NUMBER=
|
|
EIMS_SELLER_PHONE=
|
|
EIMS_SELLER_EMAIL=
|
|
EIMS_SELLER_REGION=
|
|
EIMS_SELLER_WEREDA=
|
|
# Optional seller address parts; sent as null when unset.
|
|
EIMS_SELLER_CITY=
|
|
EIMS_SELLER_SUBCITY=
|
|
EIMS_SELLER_HOUSE_NUMBER=
|
|
EIMS_SELLER_LOCALITY=
|
|
# Tax treatment — REQUIRES FINANCE SIGN-OFF. The application models no tax at all
|
|
# (invoice.taxAmount is always 0), so nothing here is defaulted: registration fails
|
|
# locally, naming the missing variables, until these are set.
|
|
EIMS_TAX_CODE=0
|
|
EIMS_TAX_RATE_PERCENT=0
|
|
EIMS_EXCISE_TAX_VALUE=0
|
|
EIMS_INCOME_WITHHOLD_VALUE=0
|
|
EIMS_TRANSACTION_WITHHOLD_VALUE=0
|
|
# Document classification and payment presentation.
|
|
EIMS_TRANSACTION_TYPE=B2B
|
|
EIMS_NATURE_OF_SUPPLIES=Service
|
|
EIMS_PAYMENT_MODE=CASH
|
|
EIMS_PAYMENT_TERM=IMMIDIATE
|
|
EIMS_UNIT_DEFAULT=PCS
|
|
# MoR numeric country code for the buyer; our companies store the country name.
|
|
EIMS_BUYER_COUNTRY_CODE=
|
|
EIMS_CASHIER_NAME=
|
|
EIMS_SALESPERSON_NAME=
|
|
# Automatic filing of issued invoices (@Cron sweep, one invoice per tick).
|
|
# Independent of EIMS_ENABLED on purpose: authentication can be live long before
|
|
# filing is. Both must be true before anything is submitted automatically.
|
|
EIMS_AUTO_SUBMIT=false
|
|
EIMS_AUTO_SUBMIT_CRON=0 */5 * * * *
|
|
# MoR rejects documents older than 3 days; the sweep will not attempt those.
|
|
EIMS_AUTO_SUBMIT_MAX_AGE_DAYS=3
|