=== Nik Shipping UA — Nova Poshta & Ukrposhta ===
Contributors: nikdimon
Requires at least: 6.7
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 1.1.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Tags: shipping, delivery, nova poshta, ukrposhta, ukraine

Nova Poshta and Ukrposhta delivery for Ukrainian shops: branch and locker pickers, live tariffs, waybills, cash on delivery and tracking.

== Description ==

Nik Shipping UA connects a WooCommerce shop to the two carriers Ukrainian
customers actually use. The buyer picks a settlement and a branch, a parcel
locker or a street address without leaving the checkout, the price comes from
the carrier for that exact parcel and destination, and the waybill is created
from the order screen in one click.

= Delivery methods =

* Nova Poshta: branch, parcel locker, courier to the door, freight branch
* Ukrposhta: post office, courier to the door
* Self-pickup from the shop's own points
* Settlement, branch, parcel locker and street search inside the checkout

= Pricing =

* Live carrier tariff for the weight and dimensions of the cart
* Volumetric weight is taken into account
* Fixed cost and a free-shipping threshold
* Handling fee as an amount or a percentage, limits by weight and order value
* Optional buffer that covers re-weighing at the branch

= Who pays for delivery =

* The shop, once an order reaches the free-shipping threshold
* The customer on the site, together with the goods
* The customer at the branch on collection — the price is shown as an estimate
  and is not added to the order
* Or the customer chooses between the last two at the checkout

= Waybills =

* Create a waybill from the order screen in one click
* Print the waybill and the 100x100 label
* Bulk create, print and delete for a list of orders
* Automatic creation when an order reaches a chosen status
* Automatic cancellation when an order is cancelled or refunded

= Money and companies =

* Cash on delivery, with a choice of who pays the transfer fee
* Several sender profiles — different sole traders, cities or branches on
  different delivery methods

= Everything else =

* Scheduled parcel tracking with automatic order status changes
* E-mail to the customer with the tracking number
* Parcel fields on products: packed weight and dimensions, cargo type, separate
  place; editable one by one, in Quick Edit or in Bulk Edit
* REST API for external systems
* A log of every call made to the carrier APIs
* Ukrainian and Russian translations, HPOS compatible

= Checkout =

The settlement and branch picker works on the classic WooCommerce checkout. The
block checkout cannot display it, so the plugin switches the checkout page to
the [woocommerce_checkout] shortcode. Only that one page is touched, the
original block markup is kept and it is restored if the option is turned off or
the plugin is deleted.

= Price =

Free. There is no licence key, no subscription and no feature gate — every
delivery method, the branch picker, waybills and tracking are available the
moment the plugin is activated. You only need your own carrier accounts (a Nova
Poshta API key, and an Ukrposhta e-commerce agreement for real Ukrposhta
waybills). Documentation in Ukrainian ships with the plugin in readme-uk.txt.

== Installation ==

1. Plugins - Add New - Upload Plugin - choose the zip - Install - Activate.
2. A "UA Shipping" entry appears in the admin menu, right under Settings.
3. Nova Poshta tab: paste the API key from the business cabinet (Settings -
   Security - My API keys), press "Check the key", then "Load sender from the
   cabinet", and confirm the sender phone and branch.
4. Ukrposhta tab: the eCom Bearer and the counterparty token are issued by
   Ukrposhta under an e-commerce agreement. Without one, switch Test mode on and
   the requests go to the Ukrposhta sandbox instead.
5. Getting started tab - "Add to a shipping zone". This creates a "Ukraine" zone
   if there is none and adds the delivery methods to it.

== Frequently Asked Questions ==

= The branch list is empty =

Nova Poshta limits how often its directory can be queried. The plugin retries
and then caches the whole list for a day. Wait a minute and reload. To clear it:
Getting started - "Clear cached directories".

= Nova Poshta answers "API key expired" =

Nova Poshta keys have an expiry date. Issue a new one in the business cabinet.

= The Ukrposhta waybill is not created =

Check that the sender client UUID, the address id and the sender postcode are
filled in on the Ukrposhta tab, and that the order has a recipient phone number.

= The settlement search for Ukrposhta returns nothing in Test mode =

The Ukrposhta address directory is a live service with no sandbox of its own and
rejects sandbox keys. Put a production key in "Address classifier Bearer" to use
the pickers while testing everything else against the sandbox.

= Where can I see what the carrier actually replied? =

The Log tab keeps the recent API calls together with the responses.

== Changelog ==

= 1.1.0 =

* The plugin is now free. The whole licence layer has been removed: there is no
  more Licence tab, no licence key and no subscription, and every feature —
  delivery methods, the branch picker, live tariffs, waybills and tracking — is
  available immediately on activation. Nothing about how the plugin works day to
  day has changed.
* Upgrading from an older paid release that had been stood down over a lapsed
  licence: the plugin picks itself back up automatically on the first load —
  the checkout page and the scheduled tracking are restored, and every setting,
  waybill and parcel record is kept exactly as it was.
* Removed the bundled licence client (nikd-license) from the package.

= 1.0.37 =

* Fixed: on a phone the order summary stuck to the top of the screen and rode
  over the form as the buyer scrolled down it. The summary is only meant to
  follow the page on the two-column layout, so the sticky rule now lives in the
  wide-screen media query instead of being written after the rule that switches
  it off.

= 1.0.36 =

* Added: the delivery details can now be filled in by hand on the order screen —
  method, settlement, branch or street, recipient name and phone — with the same
  settlement and branch search the checkout has. An order taken over the phone,
  or one where the customer changed their mind about the branch during the call,
  no longer dead-ends at "this order was not placed with a Nova Poshta or
  Ukrposhta method": type what was agreed and create the waybill from there.
* The entered details are written to the order exactly as the checkout writes
  them, so the order screen, the delivery block in the e-mails, the waybill and
  the tracking all treat such an order as an ordinary one. The recipient goes
  onto the order itself, and is checked against the carrier's rules before
  anything is saved.
* "Save details only" stores the destination without booking a parcel — for a
  call where the shipment is agreed but not going out yet, or to let automatic
  creation pick the order up later.
* The waybill box now opens at the top of the main column of the order screen
  instead of the narrow sidebar — delivery is the first thing a Ukrainian shop
  deals with on an order, and the box carries a form now. Drag it elsewhere and
  WordPress remembers that, as before.
* Fixed: a CSS or JS fix shipped between releases never reached a browser that
  already had the file, because the assets were versioned by the plugin version
  alone. They now carry their own modification time.
* Fixed: the "Create waybill" button on the order screen did nothing at all. The
  box opened a form of its own inside WooCommerce's order form, and a form inside
  a form is dropped by every browser — so the button belonged to the order form
  and posted the order screen back to itself instead of reaching the plugin.
  Waybills could only ever be created automatically or in bulk. The form now
  lives outside the order form and the controls are tied to it by id.

= 1.0.35 =

Findings of a full pass over the plugin. All four are the same kind of fault: the
plugin was right most of the time and quietly wrong in one configuration.

* Fixed: the fallback weight and dimensions from the Delivery tab were being run
  through the shop's unit conversion, although the fields are labelled in kg and
  cm. On a shop measuring in grams a 0.5 kg default became 0.5 g, and every parcel
  whose product had no weight of its own was quoted and declared far too light.
  Product weights, which really are in the shop's unit, are still converted.
* Fixed: a free-shipping threshold of 0 made every order free and billed to the
  shop. Zero now means "switched off", which is what an owner typing it intends —
  the previous reading only showed up on the carrier invoice.
* Fixed: the "some values were corrected before saving" notice never appeared on
  the Pickup, Fields, Statuses and Automation tabs. A working variable was reused
  and overwrote the list, so an unusable number typed on those tabs was silently
  replaced with no word to the owner.
* Fixed: a bulk print of several Ukrposhta orders returned the sticker for the
  first one and dropped the rest without saying so — Ukrposhta issues one document
  per parcel. It now says which orders to print one at a time.
* Fixed: the API log could grow to hundreds of megabytes. A directory answer is
  tens of kilobytes and every checkout wrote several, all kept at full length for
  two weeks. Successful calls are now trimmed to 2 KB (failures are still kept
  whole, they are what support reads) and the table has a 5000-row ceiling.
* Fixed: customer notifications for an order with no e-mail — a phone order or a
  one-click order — were skipped silently. The order now says so once, so a shop
  wondering why the tracking number never arrived can see why.
* Hardened: the Ukrposhta document relay passed the carrier's own Content-Type
  through and rendered the response inline. Anything that is not a document is
  now handed over as a download, with nosniff.

= 1.0.34 =

* Added: the recipient details are now checked against the carrier's own rules
  before the order is placed. Nova Poshta refuses a name written in Latin letters
  or carrying digits, and books nothing without a surname — until now that only
  surfaced hours later, as "LastName has invalid characters", when the shop tried
  to print the waybill and could no longer ask the buyer how their name is spelled.
  The checkout now says so in the field itself, in the buyer's language, while it
  can still be fixed. Ukrposhta accepts Latin names and is left alone; self-pickup
  is not checked at all. The phone is validated as a Ukrainian number for both.
* Added: the same check runs one last time before the payload leaves for the
  carrier, so an order created in the admin or imported from elsewhere reports
  which order field to fix instead of the carrier's raw error text.
* Fixed: a bulk "create waybills" run that failed on some orders reported only how
  many had failed. The reason is now written into each of those orders as a note,
  where the rest of the waybill history already is.
* Added: Ukrposhta refuses a name part of a single character, so that is checked
  too — for Ukrposhta only, since Nova Poshta accepts one letter.
* Filters: nik_ship_validate_recipient to switch the checks off,
  nik_ship_strict_name_carriers to change which carriers demand Cyrillic.

= 1.0.33 =

* Fixed: the shipping line no longer shows the plugin's own bookkeeping to the
  buyer. Keys such as nik_ship_option or nik_ship_weight were printed on the
  thank-you page, in the account order view, in the order e-mail and on the admin
  order screen. Everything worth saying is already in the delivery method label.
* Fixed: the preview in the e-mail designer showed "critical error" instead of
  the message. It asked WooCommerce for the inline-CSS step on the wrong object —
  the step belongs to a single message, not to the mailer that owns them all.
  Sending was never affected, only the preview.

= 1.0.32 =

* Fixed: on the Getting started tab the delivery options are now ticked
  according to what is really in the Ukraine shipping zone. They used to be
  hard-coded, so the freight branch came back unticked after every page reload
  and looked as if it had never been added.
* The result of "Add to a shipping zone" now names the options and separates
  what was added, what was already there and what was refused. Refused options
  no longer report success in green: an option whose carrier has no key yet is
  shown as skipped, and its tick box is disabled with the reason next to it.
* Housekeeping: settings left behind by older versions are dropped during the
  upgrade instead of being carried forever, and deleting the plugin now also
  removes the stored licence record and the plugin's own transients.

= 1.0.31 =

* The e-mail design now covers WooCommerce's own order messages as well — new
  order to the shop, processing, completed, refunded, invoices. The logo,
  colours, corner radius and footer are applied through WooCommerce's own
  settings rather than by replacing its templates, so the customer sees one shop
  instead of two and nothing breaks when WooCommerce updates. WooCommerce's
  wording and order table are untouched; the preview has an entry for them and
  the whole thing can be switched off.
* Licence client 1.1.0: a failed renewal payment no longer switches the plugin
  off on the same day. The paid period ends exactly when the card is charged for
  the next one, so a declined card used to look like an expired licence within
  hours. The plugin now keeps working for a week while the payment is retried,
  counts down in the admin and links straight to the card settings, and warns a
  week before the renewal date as well.
* When the licence stops being confirmed the plugin now steps aside instead of
  half-working: it stays installed and activated, keeps every setting, waybill
  and parcel field, hands the checkout page back to whatever the shop had, and
  stops the scheduled carrier polling — leaving plain WooCommerce as if the
  plugin had never been installed. The admin says what happened and reminds the
  shop to have a shipping method of its own. Activating the key puts everything
  back exactly as it was.
* New "E-mails and notifications" tab holding everything the plugin writes, with
  a designer shared by every message: logo, colours, corner radius, signature
  and footer links, a live preview and a test send. Each message has its own
  subject and body with {order_number}, {tracking_number}, {carrier} and other
  placeholders; leave them empty to keep the built-in wording. Two messages are
  new: "parcel ready for collection" to the customer, and — to the shop — a
  warning when the carrier refuses a waybill or a parcel starts coming back.
  The notification settings moved here from the Automation tab.
* Every settings field is now validated. Letters typed into a price, a negative
  weight, a 500% buffer or a half-written phone number are corrected to
  something the carriers accept, and the screen says what was changed. Numeric
  fields carry their range, so the browser refuses bad input up front.
* By default the customer now chooses at the checkout whether to pay for
  delivery on the site or to the carrier on collection.
* Automatic waybills now work out of the box and can be triggered by several
  order statuses instead of one. "Processing" and "On hold" are both ticked by
  default, because online payments land on the first and bank transfer, cheque
  and cash on delivery land on the second — with a single trigger status, shops
  taking payment on delivery never got a waybill at all and nothing said why.
  An existing trigger status is kept on upgrade and "On hold" is added to it.
* The order screen now says what automatic creation is going to do with this
  order: which status it is waiting for, or that it is waiting for payment. The
  same reason is written into the order notes when a waybill is held back.
* Delivery payment: the shop chooses whether the customer pays for delivery on
  the site or to the carrier on collection, or leaves the choice to the customer
  at the checkout. When paid on collection the price is shown as an estimate,
  stays out of the order total, and the waybill is billed to the recipient. A
  free-shipping threshold still wins over both. The old "who pays for delivery"
  setting on the Nova Poshta tab is replaced by this and migrates automatically.
* The cash-on-delivery transfer fee is now part of the delivery price quoted at
  the checkout when the shop is the one paying it. It used to be billed by the
  carrier after the fact and quietly covered by the shop.
* Fixed rate caching: WooCommerce no longer shows a price calculated for the
  previous branch, payment method or shop settings.
* Ukrposhta tracking: DELIVERING (a parcel still travelling or waiting at the
  office) was read as delivered, so an order could complete itself before the
  customer collected it — and never correct itself, because finished waybills
  are no longer polled. RETURNING had the same problem. Statuses are now matched
  against the carrier's documented list and shown in words.
* Ukrposhta: the address directory now works with sandbox credentials too.
* Sender profiles now also drive the price calculation, so a method that ships
  from another city is quoted from that city. The profile's postcode field is
  finally used.
* New "how parcels leave the shop" setting: handed over at a branch, or
  collected by a courier from the shop's address. Enables the Nova Poshta
  Address- service types and the Ukrposhta D2W / D2D types.
* Parcel fields can be edited from the products list, in Quick Edit and in Bulk
  Edit. Every bulk field defaults to "no change".
* The tracking interval is now really configurable; it used to be stored and
  ignored while the cron always ran hourly.
* Deleting the plugin restores the block checkout page and removes its leftover
  options.
* The API log is now trimmed after exactly the configured number of days.
* REST read access is checked against a capability WooCommerce actually grants.
* The checkout page is put back on the classic shortcode automatically if
  something moves it back to blocks, with a warning in the admin.
* A shop running the block checkout is warned by the plugin itself, on its own
  settings screen and in the admin, instead of through WooCommerce's generic
  "incompatible extension" banner. That flag covers the Cart block as well, and
  the cart has never been a problem here — only the checkout needs the classic
  template, which the plugin sets up and repairs on its own.
* Tested with WooCommerce 10.9 and WordPress 7.0. WordPress 6.7 is now the
  minimum: translations load through the Domain Path header instead of the
  deprecated load_plugin_textdomain() call.

= 1.0.30 =

* Automatic waybill cancellation when an order is cancelled or refunded.
* E-mail to the customer when their order is cancelled.
