=== DigiExpress (دیجی‌اکسپرس) ===
Contributors: digiexpress
Tags: woocommerce, shipping, delivery, iran
Requires at least: 6.4
Tested up to: 6.7
Requires PHP: 8.1
WC requires at least: 8.0
WC tested up to: 9.4
Stable tag: 1.0.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

افزونهٔ ثبت خودکار ارسال سفارش‌های ووکامرس با سامانهٔ حمل‌ونقل دیجی‌اکسپرس.

== Description ==

دیجی‌اکسپرس فروشگاه ووکامرسی شما را به سامانهٔ حمل‌ونقل دیجی‌اکسپرس متصل می‌کند و ثبت
ارسال سفارش‌ها را خودکار می‌سازد.

امکانات کلیدی:

* ورود یکپارچه (SSO) و نگهداری رمزنگاری‌شدهٔ توکن با تازه‌سازی خودکار اتصال.
* فیلدهای پرداخت اختصاصی: انتخاب الزامی استان و شهر از کاتالوگ، نشانی کامل و کد پستی،
  به‌همراه نقشه، موقعیت فعلی، پلاک و واحد اختیاری و قابل پنهان‌سازی.
* تبدیل خودکار ارقام فارسی به انگلیسی در فیلدهای نشانی، پلاک، کد پستی و موبایل.
* ثبت خودکار یا دستی ارسال، نگاشت اندازهٔ جعبه، همگام‌سازی وضعیت و تلاش مجدد خودکار.
* سازگار با پرداخت کلاسیک و بلوکی ووکامرس و مستقل از قالب.

== Installation ==

۱. فایل zip افزونه را از مسیر «افزونه‌ها ← افزودن ← بارگذاری افزونه» نصب و فعال کنید.
۲. مطمئن شوید ووکامرس نصب و فعال است (پیش‌نیاز افزونه).
۳. از منوی «دیجی‌اکسپرس» وارد شوید، حساب خود را متصل کنید و چهار مرحلهٔ راه‌اندازی را کامل کنید.
۴. برای اجرای به‌موقع کارهای پس‌زمینه (ثبت ارسال و همگام‌سازی وضعیت)، یک cron واقعی روی سرور تنظیم کنید — بخش «پرسش‌های متداول» را ببینید.

== Frequently Asked Questions ==

= آیا برای استفاده به ووکامرس نیاز است؟ =

بله. این افزونه برای ثبت ارسال سفارش‌ها به ووکامرس وابسته است و بدون آن فعال نمی‌شود.

= افزونه با کدام نسخهٔ PHP کار می‌کند؟ =

PHP نسخهٔ ۸٫۱ و بالاتر.

= چطور مطمئن شوم ثبت ارسال و همگام‌سازی وضعیت به‌موقع اجرا می‌شوند؟ =

وردپرس کارهای زمان‌بندی‌شده را تنها هنگام بازدید از سایت اجرا می‌کند؛ بنابراین در
ساعت‌های کم‌بازدید ممکن است ثبت ارسال یا به‌روزرسانی وضعیت با تأخیر انجام شود. برای
اجرای مطمئن و مستقل از ترافیک، یک cron واقعی روی سرور تنظیم کنید:

۱. در فایل wp-config.php این خط را اضافه کنید:
`define('DISABLE_WP_CRON', true);`

۲. در crontab سرور هر یک دقیقه این دستور را اجرا کنید (example.org را با نشانی سایت خود جایگزین کنید):
`* * * * * curl -s 'https://example.org/wp-cron.php?doing_wp_cron' >/dev/null 2>&1`

روی میزبانی اشتراکی که به crontab دسترسی ندارید، از یک سرویس cron بیرونی (مانند
cron-job.org) استفاده کنید که هر یک دقیقه همان نشانی wp-cron.php را فراخوانی کند. بازهٔ
یک دقیقه کافی است؛ بازه‌های کوتاه‌تر به‌دلیل قفل داخلی cron وردپرس سودی ندارند.

راهنمای کامل (تنظیم نرخ همگام‌سازی، HPOS و بهینه‌سازی بار) در فایل docs/cron-and-scaling.md است.
== Changelog ==

= 1.0.0 =
* DigiExpress delivery no longer disappears from checkout while the DigiExpress API is
* Orders placed while DigiExpress is unreachable are queued instead of being dropped
* An outage no longer consumes an order's registration retry budget. Transport errors,
* A hard authentication failure is no longer permanent. An hourly probe retries the
* Re-connecting after an authentication failure no longer asks for the pickup address
* Shipment registration retries are faster and more numerous: 60s, 120s, then 300s
* Cash-on-delivery is now hidden whenever a DigiExpress rate is chosen, including while

= 0.9.130 =
* DigiExpress shipment cancellations now remain shipment-only and no longer change the

= 0.9.129 =
* Admin-triggered first-time shipment registration now starts Action Scheduler's

= 0.9.128 =
* Fixed the DigiExpress tracking meta box on classic WooCommerce order screens, where
* Dismissing the DigiExpress admin warning now hides it immediately while its dismissal

= 0.9.127 =
* Added a default-on order-flow setting that exposes DigiExpress registration controls on
* After registration is queued, the manual modal now closes immediately and the order

= 0.9.126 =
* Added a unified manual shipment-registration action beside normal registration on the
* Manual parcel weight accepts positive decimal input and is normalized to at least one
* Removed the separate pickup-time panel action; pickup timing is collected as part of the
* Order-panel registration now queues DigiExpress network work and returns immediately,
* Positive sub-gram product weights are sent as one gram instead of being replaced by the
* Registration recovery now requeues attempts left in `submitting` for more than ten

= 0.9.125 =
* Added per-zone DigiExpress title, description, minimum-order, free-shipping threshold,

= 0.9.120 =
* Shipment destinations now send the selected human-readable `city_name` and `state_name`

= 0.9.110 =
* Added bounded background maintenance jobs for registration recovery, dashboard metrics,
* Added native WooCommerce order-status registry support, including active third-party
* Added opt-in customer shipment notifications with per-shipment duplicate suppression.
* Checkout and address persistence are now entirely native to WooCommerce and the active
* DigiExpress now resolves destination locality from `full_address`; new shipments no
* Invalid or empty postcodes are omitted instead of blocking checkout or registration.
* Background shipment-status dispatch now runs every five minutes without polling the same
* Shipment registration retry backoff is capped at one hour.
* Release ZIPs now use an explicit runtime allowlist and reject source maps, OS metadata,
* Delivery-failure customer notifications now cover final-delivery, pickup, routing, and stuck
* Stale automatic-registration statuses left by an inactive plugin are ignored at runtime.
* Deactivation now cancels every DigiExpress Action Scheduler and WP-Cron hook, including order jobs with arguments; uninstall also removes DigiExpress queue history so orphaned jobs cannot remain after deletion.
* Registration recovery avoids duplicate shipment creation when DigiExpress has saved a
* Dashboard and order-alert queries are bounded and compatible with both HPOS and legacy
* Removed DigiExpress-specific checkout fields, validation, persistence, storefront assets,
* Removed obsolete checkout configuration and city-management admin surfaces.
* Removed the legacy `origin_platform` field from shipment registration payloads.

= 0.9.98 =
* Shipment declared values sent to DigiExpress are now normalized to rials from the WooCommerce store currency: rial stores send the amount unchanged, toman stores send the amount multiplied by 10, and unknown currencies default to rial semantics.
* The manual registration modal now shows and stores the declared shipment value in rials, with styling aligned to the rest of the order-panel inputs.
* Shipping-price settings now label the field as store-currency based and show the current WooCommerce currency beside the activation controls.

= 0.9.97 =
* Order alert counts, dashboard stats, and the status-sync dispatcher no longer match *every* order on stores using the legacy (non-HPOS) order tables. WooCommerce's classic `wc_get_orders()` silently drops a caller-supplied `meta_query`, so DigiExpress status filters returned all orders — e.g. the "needs attention" banner showed the full order count, including orders that never used DigiExpress. A scoped filter (`LegacyOrderMetaQuery`) re-injects our `meta_query` on the CPT store, limited to `_digiexpress_*` keys; HPOS stores are unaffected.
* Shipment status order notes are now de-duplicated by their visible label instead of the internal status code, so a shipment is no longer logged repeatedly when DigiExpress moves it through several distinct status codes that share one Persian label (e.g. «در حال بررسی»). The exact status code is still recorded for status mapping.

= 0.9.96 =

= 0.9.95 =
* The weekly working-days schedule is no longer wiped to "all days off" when saving the business-hours card under a policy that hides the working-hours panel (immediate / manual per-order). A hidden marker lets the save handler tell "panel hidden" from "all days unchecked" and preserve the stored schedule; `BusinessHoursCalculator::config()` also auto-heals an all-disabled schedule back to defaults (Saturday–Wednesday enabled) so already-affected installs recover on read.
* Under the manual per-order cargo-ready policy, the order "Register Shipment" action now opens the Preparation-time modal and requires the admin to choose a pickup-ready time, instead of silently registering with the API's default "now" time. The schedule option is hidden in this mode and a time must be picked before registering.

= 0.9.94 =

= 0.9.93 =

= 0.9.92 =
* Expanded auth and refresh-token test coverage across `TokenRefresher`, `TokenStore`, and admin REST auth endpoints, including cooldown, transient failure, hard-fail, success, no-token, and new-login recovery paths.

= 0.9.91 =
* Hardened DigiExpress session refresh so one refresh-token `400/401/403` no longer disconnects the merchant; hard-fail now requires 10 spaced-out rejections, longer refresh/lock windows, and logs diagnostics.
* Checkout now maps DigiExpress province selections to WooCommerce's registered Iran state codes before writing native checkout/order state fields, preventing WC state validation failures for known province label differences.
* The checkout city/province picker now mounts above the street-address field and retries browser geolocation with a coarse fallback before showing an error.
* Refined Persian admin/help copy around logistics, courier/status wording, and shipment registration notes.

= 0.9.90 =
* Dashboard analytics now render with a vendored Chart.js (kept local, no CDN):
* Reworked the dashboard status colours into a distinct per-stage palette so the
* Removed the 90-day report range; the dashboard now offers 7 and 30 days.
* Corrected the registered-shipments trend date labels on Jalali sites: the day

= 0.9.85 =
* Unified shipment status order notes: every courier/remote status change now
* Moved the order panel's sync control into the footer alongside the other
* Dropped the «Skip»/«Un-skip DigiExpress registration» bulk actions from the

= 0.9.84 =
* Improved dashboard analytics with Persian custom tooltips, compact date labels
* Refined the status breakdown chart by keeping percentages in the hover
* Updated order-preparation settings so the next-business-day policy appears
* Removed remaining cutoff-time references from order-preparation copy and

= 0.9.83 =
* Refined Persian admin UI copy for order preparation, shipment registration,
* Rewrote the Help page with simplified Persian documentation and removed the

= 0.9.82 =
* `cargo_ready_date_time` is now sent to the DigiExpress API as Tehran local time with explicit `+03:30` offset instead of UTC, preventing a 3.5-hour scheduling shift.
* Dashboard KPI cards and donut chart now use the same mutually-exclusive pipeline buckets (registered-idle → awaiting pickup → in transit → delivered → pending → failed → needs action), eliminating double-counting and label mismatch between the two views.
* Cancelled and refunded WC orders are no longer excluded from background status-sync dispatch; sync continues until DigiExpress confirms the cancellation via a terminal remote status.
* `SyncShipmentStatusJob` no longer treats a `cancelled` or `refunded` WC order status as a reason to stop sync; only `completed` and `failed` remain as WC-level stop signals.
* After a successful shipment revive, an immediate `SyncShipmentStatusJob` is scheduled so the new DigiExpress status is fetched even when the WC order is still in `cancelled` state.

= 0.9.81 =
* Cancel and revive admin actions no longer add synthetic shipment-status events to the DigiExpress timeline; courier status changes now come from DigiExpress sync.
* Successful revive now re-enables automatic shipment status sync after a previous failed/terminal collection state, without adding a synthetic timeline event.

= 0.9.80 =
* Release archives now exclude development-only files such as tests, AGENTS.md, PHPUnit config, and Composer metadata.

= 0.9.79 =
* Block checkout now re-syncs restored DigiExpress city/province values into the WooCommerce cart address store before submission, so browser-restored checkout data can submit without manually reselecting the city.

= 0.9.78 =
* Country selection is now always hidden and forced to Iran at checkout; the old customization toggle and checkout-behaviour settings panel were removed.

= 0.9.77 =
* New **"Background jobs & cron"** section in the in-plugin Help page, plus a full
* **API circuit breaker**: after repeated transport/5xx failures the plugin briefly
* **Faster, fairer status sync.** The dispatcher now prioritises the
* HTTP request timeout reduced from 60s to 30s so a slow API cannot tie up workers.
* Alert and dashboard counts now use a single `COUNT` query instead of loading every
* On HPOS stores a composite order-meta index is added automatically (idempotent;

= 0.9.76 =
* Jalali leap-year handling: the holiday-date validator now agrees with the calendar
* Comprehensive automated test suite (PHPUnit): business-hours/cargo-ready scheduling across

= 0.9.70 =

= 0.9.69 =

= 0.9.59 =
* Shipment registration now sends `origin_platform: "wordpress"` so DigiExpress can segment WooCommerce-originated orders on its platform.
* All DigiExpress API calls now send a standardized `User-Agent` (`DigiExpress-WordPress/{plugin} (WordPress/{wp}; WooCommerce/{wc}; PHP/{php}; +{site_url})`), overridable via the `digiexpress_http_user_agent` filter.

= 0.9.55 =
* Order panel: confirmation dialogs for **Cancel shipment** and **Resubmit (revive)**. Each
* Order notes: after a successful registration the note now includes a **direct link to that
* Help & documentation: new "Actions on the order edit screen" section; documented the
* Order detail: the shipment status now shows the **live courier status** — the same chip as the
* Order panel: the destination province, city, and map pin are now **read-only / informative**.
* Order panel: **"Ship via another method"** (skip) now appears only while the order is in the
* Preparation time: removed the unreliable "~5 minutes" wording everywhere — DigiExpress rounds
* Order-flow preview: removed the duplicated per-scenario note for the immediate and
* Modals: themed the "Close / Back" button to match the plugin UI instead of the default

= 0.9.50 =

= 0.9.49 =

= 0.9.48 =

= 0.9.47 =

= 0.9.46 =
* Order panel: redesigned the failure-reason box (themed danger notice with icon) and replaced
* Order panel: themed the action buttons (rounded brand pills); hide "Choose box size" once a
* Diagnostic report: replaced the "email to support" flow with a **downloadable JSON file**
* Registration: a **network/transport failure** while fetching the box size now marks the order
* Logger now redacts secrets **on read** as well as on write, so log entries captured before a

= 0.9.36 =

= 0.9.35 =

= 0.9.34 =

= 0.9.33 =

= 0.9.32 =

= 0.9.25 =
* Admin dashboard analytics: live KPI cards (registered, pending, needs-attention,

= 0.9.24 =
* Country: restrict WooCommerce selling + shipping countries to Iran only at runtime

= 0.9.23 =
* Checkout: force billing/shipping country to Iran on block checkout (parity with classic).
* Checkout: backfill `city_name`/`state_name` server-side from the catalogue when only the
* Checkout JS: resolve city name defensively on pick; always-on console warnings + debug
* Shipment `consignor_unique_reference_id` now uses a per-site seed (`{accountId}{2 random

= 0.9.22 =

= 0.9.21 =

= 0.9.20 =
* First versioned baseline under GitLab with CI-backed release automation
