=== Arvas Product Sync ===
Contributors: arvas
Donate link: https://arvashosting.eu
Tags: woocommerce, import, inventory, variations, pos
Requires at least: 6.4
Tested up to: 7.1
Requires PHP: 7.4
WC requires at least: 8.0
WC tested up to: 11.0
Stable tag: 1.0.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Syncs a point-of-sale export into WooCommerce as variable products with colour, size and length as variations.


== Description ==

This is the free LITE edition. It contains the complete import: fetching from
the POS server, variable products with colour, size and length, brands, dry run,
diagnostics, batch processing and the protection against partial downloads.

The PRO edition adds:

* Image matching from a supplier export with images and CSV
* Category departments (Women > T-shirt, Men > T-shirt)
* Excluding brands and categories
* A button to rebuild the category structure
* GTIN/EAN checking and repair
* A server cron script for fully automatic syncing
* One year of updates and support

A one-off 99 euro. See https://verkoop.arvashosting.eu

The till system almost never runs on the same server as the web shop. This
plugin is built for that: it fetches the export from the POS server, validates
the download, and writes products natively into WooCommerce (no REST API).

What the plugin does:

* Reads a POS export (CSV/TXT without a header row, 73 columns)
* Filters by location (location 1 only by default)
* Builds one variable product per brand + model name, with Colour, Size and -
  where applicable - Length as variation axes
* Creates categories as a tree: department (Women/Men/Fragrances) derived from
  the category code, with the category name underneath
* Writes brands to every active brand taxonomy (WooCommerce core and/or a
  theme-specific one such as XStore)
* Fills EANs into the GTIN field
* Updates existing products instead of creating duplicates
* Runs automatically in blocks, with a time budget to prevent timeouts

== 1. Connecting to the POS server ==

Go to WooCommerce > Arvas Sync and pick an option under "Where is the file?":

= A. On THIS server (local path) =

Only if the web shop runs on the same machine where the till drops the export.
Enter the absolute path, for example /home/user/winstore. The path may point to
a FOLDER or a FILE; for a folder the plugin picks the newest file, even without
an extension.

If the path fails, a diagnostics table shows what PHP actually sees. Common
causes: open_basedir (PHP may not read outside the web root) or insufficient
permissions (folder 755, file 644).

= B. On ANOTHER server (the usual case) =

The plugin fetches the file, stores it in a protected cache folder, and
processes it locally.

Fill in: protocol, host, port, path to the file (NOT a URL), username and
password. The URL field is only for HTTPS mode.

Protocol choice, in this order:

1. SFTP (port 22, encrypted). The plugin tries the PHP ssh2 extension and falls
   back to cURL with sftp support. A username in email form is usually an FTP
   account without SSH; SFTP will then time out.
2. FTPS (port 21, encrypted). Usually the right choice for an FTP account.
3. FTP (unencrypted). Only when nothing else works.
4. HTTPS, if the export is reachable over a web address.

"Capabilities of this server" lists which routes are available.

= Finding the right path =

FTP accounts are almost always locked into their own home folder. What the
server knows internally as /home/user/winstore/Export.txt is often simply
/Export.txt for the FTP account. Use "Browse remote folder": enter / and click
"Show folder" to see where the account lands and how files are named (case
matters).

= Password =

The input field is fine. To keep the password out of the database, set it in
wp-config.php instead:

    define( 'ARVAS_WOO_SYNC_REMOTE_PASS', 'the-password' );

This constant takes precedence over the input field.

= Protection against broken downloads =

A partially fetched file is dangerous: missing articles would count as "gone
from the feed" and have their stock set to zero. The plugin therefore downloads
to a temporary file and only puts it to use after validation. The import aborts
if the file is smaller than 1 kB, has fewer than 10 lines, or has shrunk by more
than the configured safety threshold (40% by default) compared to the last
successful run.

== 2. Running ==

= First import =

1. Leave Dry run ON. Nothing will be written.
2. Enter a handful of article numbers under "Only these article numbers".
3. Click "Test import now" and review the report.
4. Looks right? Turn Dry run off and click "Run real import now" for those same
   numbers.
5. Check the result in WooCommerce, then clear the test field.

= Batches and time budget =

A full catalogue does not fit in one PHP request. The plugin processes a block
of products per run (40 by default) and stops itself after the time budget (20
seconds by default), well below the web server timeout. The position is saved;
the next run continues where this one left off. After an incomplete pass the
plugin schedules a follow-up run itself.

The progress bar shows which product is next. "Pass completed" means the whole
catalogue has been visited.

= Running automatically =

The plugin schedules a 30 minute WP-Cron. However, WP-Cron only fires when
someone visits the site, so on a quiet shop a sync may not run at all.

The Pro edition ships a ready-made server cron script with locking and logging
that runs outside the web root.

= Speed =

Products whose stock, price, name, EAN and attributes are unchanged are skipped.
After the first full pass, runs are therefore short.

== 3. Product structure ==

= Variable products =

Everything becomes a variable product: one product per brand + model name, with
Colour, Size and (for jeans) Length as variation axes. No grouped products are
created.

If two article numbers within the same model carry the same colour name - two
shades of brown, or the same model from two seasons - the plugin disambiguates
the label automatically: first with the supplier colour ("Brown (Camel)") and if
needed with the article number appended. Without that, WooCommerce would reject
the second variation.

The parent SKU is the model name from the feed by default. The variation SKU is
the internal barcode, which is always filled and unique.

= Categories =

Products are placed in the category supplied by the POS feed.

Pro adds a department above that, based on the category code, giving you
Women > T-shirt and Men > T-shirt instead of two separate categories. Pro also
adds brand and category exclusions, and a button to rebuild the structure
entirely.

= Brands =

The plugin writes to every active brand taxonomy. If a theme with its own brands
(such as XStore) runs alongside WooCommerce core, both are filled, so the brand
is correct in Woo as well as in the theme.

= Exclusions =

Brands and categories that do not belong in the shop go into "Exclude brands"
and "Exclude categories". Products that already exist are left alone; remove
those yourself.

= Manual edits =

The product title is only set for new products, so manually edited titles stay
put. Categories do follow the feed; turn on "Protect categories" if you manage
the structure yourself. Stock, price and variations are always updated.

== 4. GTIN / EAN ==

EANs from the feed go into the variation's GTIN field. WooCommerce allows a GTIN
on only one product; if an older product still holds the code, the plugin skips
the field and reports it - the variation itself is still saved.

Pro includes a check showing how many variations have a GTIN, with a button to
fill in missing codes.

If the till supplies no EAN for certain articles, the GTIN field stays empty.
That is a data issue in the till, not an import error.


== 5. Existing catalogue ==

On activation the plugin determines the mode: fresh (no products) or migrate
(products already exist). Existing products are recognised by SKU, by a durable
internal key, and otherwise by title - including drafts. This prevents
duplicates even when SKUs or titles have changed in the meantime.

If a run shows only new products and nothing updated while the catalogue is
already populated, the plugin warns you: that indicates duplicates.

Products from an earlier setup (SKU starting with ART- or GRP-) can be set to
draft with "Clean up leftovers"; their SKU and GTIN are then released. Nothing
is deleted.

== 6. Language ==

The plugin ships with an English translation of the admin interface
(languages/arvas-woo-sync-en_US.mo). WordPress selects it automatically based on
the site language under Settings > General.

The source language is Dutch. To translate into another language, use
languages/arvas-woo-sync.pot as the starting point and place the resulting .mo
file in wp-content/languages/plugins/ so it survives plugin updates.

== Frequently Asked Questions ==

= Does the plugin overwrite my own product titles? =

No. The title is only set for new products. Turn on "Overwrite titles" if you
want the feed to take precedence instead.

= What happens if the supplier delivers a partial file? =

The import is aborted. The plugin checks the file size and line count and
compares them with the previous successful run. On a suspicious drop, nothing
in the shop changes.

= Does this work when the till runs on a different server? =

Yes, that is the usual situation. The plugin fetches the file over SFTP, FTPS,
FTP or HTTPS, stores it in a protected folder and processes it locally.

= My theme has its own brands system. Does that work together? =

Yes. The plugin detects every active brand taxonomy and writes to all of them,
so the brand is correct in WooCommerce as well as in the theme.

= Why does the import seem to stall on a large catalogue? =

That is not a failure. The plugin works in blocks and stops itself before the
web server timeout. Progress is saved and the next run continues. Follow the
progress bar rather than the report.

= Why do some GTIN fields stay empty? =

Because the till supplies no EAN for those articles. That is a data issue in
the till, not an import error.

== Screenshots ==

1. Settings: connection to the POS server, with a test button and diagnostics.
2. Environment scan: WooCommerce version, brand taxonomies and existing attributes.
3. Progress bar and the tally per run, with the reason when a run stops early.
4. Report of an import: counts, intended actions and any conflicts found.

== Changelog ==

= 1.0.0 =
* First complete release.
* POS export over SFTP/FTPS/FTP/HTTPS or a local path, with download validation.
* Variable products with Colour, Size and Length; automatic disambiguation of
  duplicate colour names.
* Category tree based on the category code, with configurable department ranges.
* Brands written to every active brand taxonomy.
* GTIN/EAN handling with conflict resolution and a separate repair tool.
* Batch processing with a time budget, progress bar and self-chaining runs.
* Brand and category exclusions; protection of manually edited titles.
* English translation of the admin interface, with a .pot file for other
  languages.

== Upgrade Notice ==

= 1.0.0 =
First public release.
