# Angry Offers PRO — Full Documentation Structure

> Extracted from `wp-content/plugins/angry-offers-pro/includes/class-aop-documentation.php`
> (the WordPress plugin's in-admin "Documentation" page) for use as the functional/content
> spec of an equivalent Laravel project. Source is entirely static English text in the
> original file (Greek code comments only), reproduced here in the same structure and order.

## How the original page is built (for reference)

- One admin page (`Angry Offers PRO → Documentation`), sticky left sidebar + single
  scrolling content column, JS-driven active-section highlighting via `IntersectionObserver`.
- Sidebar is a nested structure: **Section → Topics** (`get_sections()`), in the exact
  order sections/topics should be presented.
- Each topic is either "written" (full content, one `render_topic_*()` method per topic)
  or a "stub" ("Documentation for this section is coming soon.").
- Helper renderers: `render_screenshot()` (image + caption, `assets/img/docs/*.webp`,
  shows a placeholder box if the file is missing) and `render_youtube_video()` (embedded
  YouTube player from a URL or bare video ID, placeholder if empty/unrecognized).

Below: full section/topic tree, then the complete written content for every topic that
has it, verbatim from the PHP.

## Section / Topic Tree

1. **Overview**
   - Introduction
2. **Core Records**
   - Suppliers
   - Clients
   - Price List
   - Attributes
3. **Sales Workflow**
   - Client Offers
   - Orders
   - Proforma & Invoices
   - Payments
   - Online Payments
4. **Supplier Workflow**
   - Supplier Offers
   - Supplier Orders
   - Supplier Payments
5. **Automation**
   - Reminders & Notifications
   - PDF Documents
   - Email Messages
6. **Insights**
   - Dashboard
   - Reports
7. **Configuration**
   - Settings
   - Translations

All 20 topics above are "written" (full content) in the current plugin — there are no
stub topics left.

---

## Overview

### Introduction

Angry Offers PRO is a self-contained sales & purchasing management system built directly
on top of WordPress. It replaces spreadsheets for a business that needs to quote products
or services to clients, turn accepted quotes into orders, invoice and get paid for them —
while also tracking its own purchasing from suppliers on the other side of the same deal.

The plugin is organized around two mirrored workflows that share the same building blocks
(Price List and Attributes), plus an automation layer (reminders, PDF documents, emails,
online payments) and an insights layer (Dashboard, Reports).

*(Original page embeds a full walkthrough video here: https://youtu.be/5Wtzsf8dK-E)*

**The client-side workflow** — the main, revenue-facing flow:

1. **Client** — a company or person you sell to.
2. **Client Offer** — a quote sent to that client, built from Price List lines (with
   optional Attributes per line). The client can accept it online, without logging in.
3. **Order** — created automatically once an offer is accepted (or manually). This is the
   record that everything downstream hangs off: invoicing, payments, reminders.
4. **Proforma & Invoice** — billing documents generated from the Order. The Proforma is
   typically sent first (e.g. to request an advance payment), the Invoice once the sale
   is finalized.
5. **Payments** — amounts recorded against an Order, either logged manually by the admin
   or collected automatically through an **Online Payment** gateway (Stripe, PayPal,
   WooCommerce Checkout, or Bank Transfer) from a "Pay Online" link on the Proforma email.

**The supplier-side workflow** — a "reverse" bookkeeping flow for tracking your own
purchasing, mirroring the client side one-to-one:

1. **Supplier** — a company or person you buy from.
2. **Supplier Offer** — a quote you received from a supplier.
3. **Supplier Order** — what you actually ordered from them.
4. **Supplier Payment** — what you paid them, and what's still outstanding.

**Shared building blocks** — **Price List** is the product/service catalog: every offer
and order line is built from it. **Attributes** let you attach structured, reusable specs
to a line (e.g. dimensions, materials) — either freely filled in ("Global") or picked from
a ready-made preset — and they carry through automatically to the Order, PDF documents,
emails, and the client's online acceptance page.

**Automation layer** — **Reminders & Notifications** run daily via WP-Cron: one nudges
clients about offers they haven't responded to, the other chases unpaid Order balances
after an invoice was sent — both with a "Pay Online" shortcut when applicable. **PDF
Documents** and **Email Messages** are generated for every offer, proforma, invoice, and
order, using the company details and message templates configured in Settings.

**Insights layer** — the **Dashboard** gives an at-a-glance financial picture (revenue,
pending amounts, offer status). **Reports** break this down further, e.g. per client.

**Configuration** — **Settings** covers currency, default VAT, document numbering,
company details, email templates, payment gateways, and notification rules.
**Translations** lets you translate every fixed string the plugin displays (menus,
labels, emails, PDF) into your own language, without touching code.

---

## Core Records

### Suppliers

A Supplier is a company or person you buy from. It's a simple contact record on its own —
its real purpose is as the starting point of the Supplier Workflow: every Supplier Offer,
Supplier Order and Supplier Payment is linked back to one.

**List view** — Title, plus **Contact**, **Phone**, **Email** columns.

**Adding / editing a Supplier** — **Title** = the supplier's company/personal name
(appears everywhere: dropdowns, PDFs, Dashboard widgets). **Contact Details** box:

- **Contact Person** — the specific person you deal with at that company.
- **Phone** and **Email**.
- **VAT Number** and **Address**.
- **Notes** — free text (e.g. payment terms agreed with them).

None of these fields are required to save the record.

**Financial Details (read-only)** — a second box shows this supplier's activity for a
chosen **Year** (its own independent year selector): number of Supplier Orders, their
Total Amount, how much has been Paid, and the remaining Balance. Calculated automatically
from that supplier's Supplier Orders and Supplier Payments — nothing here is editable, and
it only appears once the record has been saved at least once.

**Deleting a Supplier** — standard trash/delete. Deleting a Supplier does not delete its
Supplier Offers/Orders/Payments — those remain, just without a linked supplier name.

### Clients

A Client is a company or person you sell to — the starting point of the Sales Workflow:
every Client Offer and the Order it turns into are linked back to one. Built exactly like
a Supplier, just facing the other direction of the business.

**List view** — Title, plus **Contact**, **Phone**, **Email** columns.

**Adding / editing a Client** — **Title** = client's company/personal name (used in
dropdowns, PDFs, emails, Dashboard/Reports). **Contact Details** box:

- **Contact Person**
- **Phone** and **Email**
- **VAT Number** and **Address**
- **Notes** — free text

None of these fields are required to save the record.

**Financial Details (read-only)** — same pattern as Suppliers: chosen **Year**, number of
Orders, Total Amount, Paid, Balance — calculated automatically, not editable, only shown
after first save. Also broken down per-client, across every client at once, under Reports.

**Deleting a Client** — standard trash/delete. Deleting a Client does not delete its
Client Offers/Orders/Invoices/Payments — those remain, just without a linked client name.

### Price List

The Price List is your own catalog of products and services, ready to drop into a Client
Offer line with consistent pricing instead of retyping it every time.

**Categories** — a hierarchical taxonomy (like WP post categories); filter dropdown above
the list, and the same grouping is available while picking a product inside a Client Offer.

**Description & Unit of Measure** — **Title** = product/service name. **Description**
shown inside the product-selection modal when building an offer. **Unit of Measure** =
free text with suggestions (piece, hour, m², page, project) — can type your own.

**Price Tiers** — instead of one fixed price, every entry has a table of quantity-based
pricing tiers, so the same product can have a different price (and even a different spec)
depending on how much is ordered. Example: 1,000 pcs × €0.045/pc = €45 with "matte
lamination, 2 sides", but 2,000 pcs × €0.0375/pc = €75 with a different spec — each tier
independent. Each tier has:

- **Quantity** and its own **Description** (overrides/extends the general Description).
- **Cost/Unit** — optional; type directly, or pull from an existing Supplier Offer via the
  small "+" picker (one-time copy, not a live link).
- **Cost**, **Price/Unit**, **Total**, **Profit** — calculated automatically from
  Quantity × Cost/Unit and Quantity × Price/Unit.

When later picked inside a Client Offer, the chosen tier fills in the offer line's
quantity, description and price in one go.

**Status** — **Active** switch controls whether the entry shows up in the Client Offer
product picker. Turning it off doesn't delete or affect past offers/orders — just stops
future selection (useful for discontinued products kept for history).

**List view** — columns: **Price** (every tier's quantity and price, stacked), **Unit**,
**Status** (Enabled/Disabled badge) — plus the Category filter above the table.

**Using it in a Client Offer** — the product picker has a "Price List" tab (own Category
filter + text search). Picking an entry, then a specific tier, fills Product/Description/
Price in one click (one-time copy, same as picking from a Supplier Offer).

### Attributes

Attributes let you attach structured, reusable specs to a single Price List or Client
Offer line — e.g. a set of "Dimensions" (Width/Length/Height) or a plain "Color" — instead
of typing the same specs into free-text Description every time. Built once as reusable
**groups**, then applied to as many lines as you like.

**The Attribute Groups library** — lists every group: Title, its Global Fields, and how
many Presets it has, with Edit/Delete links. Creating one only asks for a title; fields
are added on the next screen.

**Editing a group** — two tabs:

- **Global Attributes** — the group's fields (e.g. Width, Length, Height). Each field can
  optionally get a **Predefined Values** list (comma-separated → becomes a dropdown) —
  leave empty and it's typed freely.
- **Preset List** — ready-made combinations covering every field at once, e.g. a preset
  "60x120x75cm" with Width=60, Length=120, Height=75 pre-filled. "+ New Preset" reads the
  group's current fields and gives empty inputs (value + optional description). A group
  with no fields at all (like plain "Color") just gets single-value presets.

**Applying an attribute to a line** — on a Price List item or Client Offer line, a
"+ Attribute" link sits under the Product field. Opens a picker to:

- Pick an existing group as **Global** — fields appear empty, filled in on the spot, or
- Pick one of that group's **Presets** — values filled in immediately, or
- Create a brand new group **on the fly** — title + comma-separated fields — added to the
  library and applied in one step, without leaving the offer.

A line can carry more than one attribute; once added, values are edited directly on the line.

**Where the values show up** — attributes on a Client Offer line carry over automatically
to the Order once created. Visible to the client everywhere the line appears: PDF
documents (Offer and Order), offer/order emails, and the online no-login acceptance page.
Scoped to the client-facing side only — Supplier Offer and Supplier Order lines don't
support them.

---

## Sales Workflow

### Client Offers

A Client Offer is the quote you send to a Client — the entry point of the entire Sales
Workflow.

**Offer Details:**

- **Client** — who the offer is for.
- **Status** — In Progress / Accepted (Won) / Rejected (Lost).
- **Valid Until**, **Send Date**, **Acceptance Date**.
- **Offer Message** — optional, specific to this offer; shown above the pricing table
  together with the General Message from Settings → Email Messages.

The main editor above these fields is free text for general description/notes.

**Offer Products** — each line has **Product** and **Description** (autocomplete from
previous entries, see Dictionary in Settings), **Units**, a separate **Cost/Unit** and
**Price/Unit** (cost = what it costs you, price = what the client pays, gap = margin), and
**Line Total**. The "+" picker fills a line from the Price List or a Supplier Offer in one
click; "+ Attribute" attaches Attributes — both one-time copies, not live links. Two more
toggles per line:

- **Active** — an inactive line stays visible but isn't counted in totals.
- **Order** — decides whether this line goes into the *next* Order created manually from
  this offer. Always starts unchecked, even on a new line. (If the client accepts via the
  Online Acceptance Link instead, the Order is created automatically for whatever they
  selected — no need to touch these checkboxes.)

Below the table: VAT/totals box (optional **Deposit** percentage, used later on the
Proforma), and a **Total Revenue (excl. VAT)** figure (price minus cost, across active
lines).

**Order History** — a Client Offer can produce more than one Order over time — every time
one is created (by admin or online acceptance), a permanent block is added showing exactly
which products/quantities went into it and when. Enables partial acceptance and repeat
orders traced back to the same original offer.

**Attached File** — same mechanism as Supplier Offer: optional reference document (e.g. a
design mockup), shown with a paperclip icon in the list.

**Send Offer:**

- Checkbox (checked by default) to generate and attach the offer's PDF.
- **Online Acceptance Link** — no-login URL for this specific offer, included in the email
  automatically, also copyable for manual sending (WhatsApp, SMS, etc.).
- **Send to Client** (shown once client has an email on file), optional "Copy me too" BCC,
  plus **Send Test** and a "Last sent" note.

**Order (sidebar)** — lists every Order already created from this offer (linked), plus a
**Create New Order** button that builds one from whichever lines currently have "Order"
checked — no need to save the offer first.

**Online acceptance (no login)** — opening the link shows the client the offer's products
as checkboxes so they can select exactly what they want and click "Accept Selected" — this
immediately creates an Order for those lines. Returning to the same link later shows
already-accepted lines as such; remaining ones can still be accepted in a further batch.

**List view** — columns: Number, Client, Products (line count), Total (with VAT), Cost
(with VAT), Revenue, Created, Status, File. Numbering configured under Settings → General
Settings → Offer Numbering.

### Orders

An Order is where fulfillment, invoicing and payment actually happen — everything before
it (the Offer) was just a quote. No "Add New" button — an Order always comes from:

- The **Create New Order** button on a Client Offer (or the client accepting online) —
  copies the checked lines.
- The **"+ New Order"** button (Dashboard or Orders list) — for a phone order with no
  prior offer: search for the Client, optionally pick one of their older Offers to copy
  still-valid lines from, or start empty and add products afterwards.

**Order Details:**

- **Client** and **Related Offer** (if any) — read-only, linked back to source offer.
- **Completion Date** and **Payment Date** — setting a Payment Date is what actually
  counts this order in revenue statistics; once set, the original offer is no longer
  counted (avoids double-counting the same sale).
- **Comments** — shown in the Client Update email.

**Order Products** — lines start as a read-only snapshot copied from the offer (Product,
Description, Attributes, source Supplier tag, Quantity, Cost/Unit, Price/Unit, Total). The
only things actively managed per line:

- **In progress / Completed / Cancelled** — three independent status switches per line. A
  cancelled line stays visible for history but drops from the Total. "Completed" lines are
  mentioned in the Client Update email.
- **Invoicing** — checkbox controlling which lines go into the *next* Proforma/Invoice.
  Once actually invoiced, shows an "Invoiced" badge with a link and drops out of the
  selection — an order can be invoiced in several batches over time (mirrors an offer
  producing several orders).
- **New Comment** — free-text note tied to that line, logged in Product Status History.

**Add Products** — a section below the table to add brand-new lines directly onto an
existing Order (e.g. the client wants to add something before it ships). Picked from the
Price List; become permanent once you click "Update".

**Cost & totals** — two boxes side by side:

- An **internal-only** profitability block — Cost, Cost with VAT, Profit, VAT Payable —
  never shown to the client.
- Client-facing totals — Subtotal, VAT, an optional **Tax Withholding** percentage
  (subtracted from Subtotal; at 0% it's omitted entirely rather than shown as zero), and
  the **Final Total**.

**Product Status History** — automatic log: every status change (after saving) or comment
addition gets a dated entry, most recent first. Each entry has its own "Visible to client"
switch — only checked entries go into the next Client Update email — and once sent, that
switch locks (can't retroactively hide something already received).

**Payments** — table of payments on account: Date, Method ("Manual" or actual gateway
name if via Online Payment), Amount — running Total Payments / Balance underneath. Below
that, **Next Online Payment Amount** sets what the client is asked to pay next via "Pay
Online" on the Proforma/Invoice: percentage of current Balance, or a fixed amount (fill in
only one). Read live on Preview/Send (no need to save first); meant to be reset to blank
after a real send.

**Client Update** — sends a status email, normally whatever lines are "Completed" plus
any comment marked "Visible to client", with Preview & Send flow. Once the order is fully
invoiced, this email also automatically includes a complete financial statement (all
products, payments, balance).

**Proforma Invoice** — sends a Proforma covering only lines currently checked in the
Invoicing column — nothing is locked in, the same or different lines can be picked again
later for the final Invoice. Same Preview & Send pattern, optional PDF attachment, and a
"Test Preview" that doesn't touch real send history.

**Invoice** — works the same way as Proforma, with its own numbering and send history,
issuing against whichever lines are currently selected for invoicing. Once every line is
invoiced, this box instead lists the Invoice(s) already issued, each linking to its own
record.

**List view** — columns: Number, Client, Offer (linked if any), Total, Paid (Payment Date
or "Pending" badge), Invoiced (Fully / Partially / Not Invoiced). Numbering configured
under Settings → General Settings → Order Numbering.

### Proforma & Invoices

Both are billing documents sent from an Order, covering whichever lines are checked in
that Order's Invoicing column — but they behave very differently once sent.

**Proforma Invoice** — sent from the Proforma Invoice box on the Order itself; **no
separate record or list** anywhere in the plugin. Every Preview/Send regenerates it fresh
from the Order's current data. Natural document for requesting a deposit or the full
amount before final invoicing; this is where the "Pay Online" button lives. Nothing is
persisted — no "old Proformas" archive; the Order is the only trace. (The Outstanding
Balance Reminder is anchored to the actual Invoice being sent, not the Proforma.)

**Invoices** — unlike the Proforma, actually sending an Invoice creates a permanent,
frozen record: snapshot of the client's name and VAT number, the exact line items included
in that batch, Subtotal/VAT/Withholding/Grand Total, and the order's Balance at that
precise moment. Never looks at the Order again afterwards — even if the Order changes
later, this record stays exactly as issued. Because invoicing can be partial, one Order
can end up with several Invoice records over time.

- No "Add New" here either — created only via an actual send from an Order.
- **Invoice Details** is read-only: Client + VAT Number, link back to source Order,
  when/to whom sent, and the frozen line table exactly as invoiced.
- **Send Invoice** on the Invoice's own edit screen resends *exactly* that original
  document regardless of anything since changed on the Order — different from the
  "Invoice" box on the Order, which issues a *new* invoice for not-yet-invoiced lines.

The invoice PDF also shows the client's "Previous Balance" — total outstanding balance
across every other invoiced Order too (each counted once).

**List view** — columns: Number, Client, Total, Balance (frozen at issue time), Issue
Date, Order (linked). Numbering independent for each: Settings → General Settings has
separate Proforma Invoice Numbering and Invoice Numbering.

### Payments

Every payment recorded against an Order gets its own record here — the client-side
counterpart of Supplier Payments. Created in one of two ways:

- **Manually** — added on the Order's Payments table.
- **Automatically** — a successful Online Payment (Stripe, PayPal, WooCommerce Checkout)
  creates one the moment the gateway confirms the charge, no admin action needed.

**What you can (and can't) do here:**

- No "Add New" — exists only because a payment was added on an Order or came via a gateway.
- **Client** and **Order Number** read-only (Order Number links to the Order).
- **Method** shows source — "Manual" or the specific gateway.
- **Amount** editable here, updates the matching row back on the Order (two-way link).
- **Date** editable *unless* it was a real-time confirmed online payment (Stripe and
  PayPal always lock it; WooCommerce Checkout locks it too except when the client's chosen
  method there isn't instantly confirmed, like bank transfer or COD). A locked date shows
  disabled with an explanation.

Deleting a record here does not remove its row from the Order's Payments table — manage
additions/removals from the Order itself.

**List view** — columns: Date, Client, Order Number (linked), Method, Amount.

**Where these numbers get used** — Dashboard's Collections stat, "Income / Expenses" and
"Paid / Unpaid Orders" tabs of the Financial Overview card, plus client breakdowns under
Reports.

### Online Payments

Lets a client pay a deposit or the full Order balance online, without manual recording.
The "Pay Online" button appears on the Proforma Invoice email (and the Outstanding Balance
reminder) once at least one gateway below is connected and Enabled. Configured under
Settings → Payment Gateways.

> Note on currency: the **Payment Currency** dropdown (EUR/USD/GBP/CHF) on this tab is
> separate from the display **Currency Symbol** under General Settings — Stripe/PayPal
> need a real ISO currency code to charge in; the symbol is purely cosmetic text.

**Stripe** — needs three values from the Stripe Dashboard (`dashboard.stripe.com`):

1. Log in (or create a Stripe account; complete business verification to go Live — Test
   mode works immediately without it).
2. Test mode switch (top-right) — **on** gives test keys (`pk_test_…` / `sk_test_…`) for
   rehearsing full payments with fake card numbers; **off** gives real
   `pk_live_…` / `sk_live_…` keys.
3. Open **Developers → API keys**.
4. Copy the **Publishable key** → plugin's Publishable Key field.
5. Reveal and copy the **Secret key** → plugin's Secret Key field (treat like a password).
6. Click **Validate** — live call to Stripe confirming the Secret Key works, nothing saved
   yet.
7. Click **Save Settings**. Only after successful Validate + Save does **Enabled** become
   available.

Optional but recommended — **Webhook Signing Secret** (safety net if the client closes the
tab right after paying, before Stripe redirects back):

1. Stripe Dashboard → Developers → Webhooks → Add endpoint.
2. Endpoint URL = the address the plugin shows under the field
   (`https://yoursite.com/wp-json/aop/v1/webhook/stripe`).
3. Select event: **checkout.session.completed**, save.
4. Reveal the webhook's **Signing secret** (`whsec_…`).
5. Paste into the plugin's Webhook Signing Secret field, Save Settings.

Test and Live are two separate sets of keys (and a separate webhook if configured) —
switching the Stripe dashboard's Test mode switch requires copying the matching keys and
re-validating.

**PayPal** — needs a **Client ID** and **Client Secret** from a PayPal "app"
(`developer.paypal.com`):

1. Log in (Business account required to go Live; personal account enough for Sandbox
   only).
2. Go to **Apps & Credentials**.
3. Pick **Sandbox** or **Live** at the top — must match the plugin's **Mode** dropdown
   (Sandbox/Live have entirely separate credentials).
4. Click **Create App**, name it (e.g. "Angry Offers Pro"), confirm.
5. Copy the **Client ID**; click **Show** next to **Secret** to reveal/copy the **Client
   Secret**.
6. Paste both into the plugin, matching the Mode picked in step 3.
7. Click **Validate** — logs into PayPal to confirm credentials, nothing saved yet.
8. Click **Save Settings** — Enabled becomes available only after successful Validate +
   Save.

To rehearse in Sandbox, use a Sandbox test buyer account (Developer Dashboard →
Sandbox → Accounts; PayPal creates default test buyer/business accounts automatically).

**WooCommerce Checkout** — if WooCommerce is active, routes the client to the existing
WooCommerce checkout (with whatever gateways are already connected there) instead of
configuring anything gateway-specific in this plugin. Just flip **Enabled** and set the
**Method Description** text shown to the client.

**Bank Transfer** — simplest option, no keys/validation. Shows the client your bank
details (the Bank Accounts text from Settings → Email Messages) and tells them you'll
confirm once the money arrives. No automatic confirmation — the payment must still be
recorded manually on the Order once received.

---

## Supplier Workflow

### Supplier Offers

A Supplier Offer is your own record of a quote or price list a Supplier gave you — a place
to keep what they offered and at what cost, to compare suppliers and pull real cost
figures into a Supplier Order later. **Purely internal** — nothing here is ever emailed to
the supplier, no online acceptance page.

**Offer Details:**

- **Supplier** — which Supplier this quote came from.
- **Offer Date** and **Valid Until**.
- **Status** — In Progress / Accepted / Rejected, set by hand (no client-facing action
  drives it — a supplier never sees this record).

The main editor above these fields is free text for general description/notes.

**Products** — line-item table: **Product**, **Description**, **Units**, **Cost/Unit**,
automatically computed **Line Total**. "+ Add Line" for more rows; VAT rate and running
totals box below (same VAT mechanism as elsewhere, editable per-offer, defaulting to the
site-wide rate). Supplier Offer lines don't support Attributes (scoped to client-facing
side only).

**Attached File** — small upload box in the sidebar for the actual document received from
the supplier (PDF, scanned quote, spreadsheet) — a reference copy independent of the
Products table. Shows as a paperclip icon in the list view.

**List view** — columns: ID, Supplier, Products (line count), Total (with VAT), Date,
Status, File.

**How this connects to Supplier Orders** — **no automatic conversion** into a Supplier
Order; created separately whenever you decide to place it. The connection is a convenience
at the line level: while adding a line to a Supplier Order, the product picker has a
"Supplier Offer" tab to choose a Supplier and one of their Offers (or search a product name
across every supplier's offers, cheapest match first) and pull in Product/Description/Cost
in one click — a one-time copy, like autocomplete, not a live link.

### Supplier Orders

A Supplier Order is what you actually ordered from a Supplier — the purchasing-side
counterpart of an Order, and the record Supplier Payments and outstanding-balance tracking
hang off. As with Supplier Offers, no automatic conversion — created manually, pulling in
lines from an existing Supplier Offer only where convenient.

**Order Details:**

- **Supplier** — who the order is placed with.
- **Order Date**.
- **Message** — optional free text shown in the email sent to the supplier (e.g. delivery
  instructions).

**Order Products** — same line-item table as a Supplier Offer: Product, Description,
Units, Cost/Unit, Line Total, VAT and totals below. Each row has a "+" picker to pull
Product/Description/Cost from an existing Supplier Offer (one-time copy, not a live link).

**Payments** — simple table on the Order: Date, Method, Amount, "+ Add Payment" for as
many rows as needed, running Total Payments / Balance box underneath. Every row is
automatically mirrored into its own read-only Supplier Payment record (what Dashboard and
Reports use for purchasing figures) — payments are managed from the Order, not the other
way around.

**Send Order** — sidebar box to email the order to the supplier — plain text/HTML, no PDF
attachment, no online acceptance link (a supplier is never expected to click anything
back):

- Shows the Supplier's email on file, or a warning to add one if missing.
- **Send to Supplier** — real send, optional "Copy me too" (BCC to Test Send Email).
- **Send Test** — sends the same content to Test Send Email to check first.
- **Last sent** — note showing when last emailed, and to whom.

**List view** — columns: Number, Supplier, Total, Paid, Balance — green "Settled" badge
once fully paid, or the remaining amount otherwise. Numbering configured under
Settings → General Settings → Supplier Order Numbering.

### Supplier Payments

Every row added in a Supplier Order's Payments table automatically gets its own record
here. Exists mainly to give the purchasing side its own dedicated list and feed the
Dashboard/Reports figures — not usually worked with directly day to day.

**What you can (and can't) do here:**

- No "Add New" button — a record only appears because a payment row was added on a
  Supplier Order and saved.
- **Supplier** and **Order Number** are read-only, the latter linking to that Order's edit
  screen.
- **Date**, **Method**, **Amount** are editable here — updating them updates the matching
  row back inside the Supplier Order (a genuine two-way link, not a one-time copy).

Deleting a record here does not remove its row from the Supplier Order's Payments table —
simpler to add/remove/edit payment rows from the Order itself for anything beyond a quick
correction.

**List view** — columns: Date, Supplier, Order Number (linked), Method, Amount.

**Where these numbers get used** — real cash-movement figures behind the purchasing side
of the Dashboard (Supplier snapshot, "Income / Expenses" tab as Expenses, "Top Suppliers by
Spend" chart), plus supplier breakdowns under Reports.

---

## Automation

### Reminders & Notifications

Two independent, fully automatic email reminders configured under Settings →
Notifications. Both off by default, run once a day in the background.

**Unanswered Offer Reminder** — nudges a client who hasn't responded to a Client Offer:

- **Send After (Days)** — days after the offer was last sent before the first reminder.
- **Message** — free text; the Offer Number and a "View Offer" button (same online
  acceptance link) are always appended automatically underneath (can't be removed/edited).
- **Repeat** — optionally keep resending every N days while the offer stays unanswered.

Stops on its own once the offer is accepted or rejected.

**Outstanding Balance Reminder** — chases an Order still owed money after an Invoice was
sent:

- **Send After (Days)** — days after the Invoice was sent while the Balance is unpaid.
- **Message** — free text; the Order Number, current Balance amount, and a "Pay Online"
  button always appended automatically ("Pay Online" only appears if at least one Online
  Payment gateway is Enabled, covering the entire current Balance).
- **Repeat** — optionally keep resending every N days until fully paid.

Timing detail: anchored to the date the Invoice document was actually emailed to the
client — not any date field that also changes on "Send Test" — so internal testing doesn't
reset the real countdown.

**Preview & Send Test** — both reminders have two buttons next to their Message field:

- **Preview** — read-only preview using sample data.
- **Send Test** — actually emails the real, currently-typed message (no save needed first)
  to the Test Send Email address.

**How the daily check works** — both reminders evaluated once a day by a single WP-Cron
job. Like any WP-Cron task, it only fires on a real site visit, so on very low-traffic
sites the check can run a bit later than exactly 24 hours. The Notifications tab shows a
"Last checked" timestamp (updates on every run regardless of whether anything was sent).

### PDF Documents

The plugin generates a PDF for a Client Offer, a Proforma Invoice, an Order Invoice, or an
issued Invoice snapshot — always as an **email attachment**, generated at send/Preview
time and deleted right after. No persistent "download PDF" file — sending again simply
regenerates from current data (or, for an already-issued Invoice, from its frozen
snapshot).

**What's on the page** — a dedicated print layout (not the email's literal HTML) built
from the same data as the matching email: logo and company details (Company Name, VAT
Number, Address, Phone, from Settings → Company Details), document number and client, line
items (including Attributes), totals, and Footer text — with Bank Accounts text added only
on a Proforma Invoice.

**Font** — rendered with bundled **Open Sans** by default (covers Latin, Greek,
Cyrillic). Settings → General Settings → Custom PDF Font lets you upload your own Regular
and Bold `.ttf` files — italic/bold-italic automatically fall back to the closest available
style rather than losing non-Latin characters.

**Logo** — pulled live from the Media Library attachment set under Settings → Company
Details — the only case the PDF generator loads a remote image, and only from this site's
own media, never arbitrary URLs.

### Email Messages

Every email the plugin sends (offers, supplier orders, proformas, invoices, client
updates, reminders) is built from the same ingredients, configured once under
Settings → Email Messages and reused everywhere.

**Anatomy of an email:**

- Header with Email Logo, Company Name, VAT Number, Address, Phone.
- The document's own title/number and, where relevant, who it's addressed to.
- The General Message (always shown) plus, on a Client Offer, that offer's own Offer
  Message.
- A pricing table of active line items (including Attributes) with Subtotal/VAT and
  (where applicable) a requested Deposit.
- On a Proforma Invoice specifically, the Bank Accounts text, just above the Footer.
- The Footer Information text at the very bottom.

**Sending mechanics:**

- **Sender Email** sets the "From" address (falls back to WP's admin email); **Test Send
  Email** is both the destination for "Send Test"/"Test Preview" and the "Reply-To" on
  real emails.
- **Copy** (BCC) available on every real send, defaulting to Settings but changeable per
  send.
- Every send records a "Last sent" date and recipient, shown next to the Send button.

**Automatic internal notification** — whenever a client accepts a Client Offer online
(full or partial), a short plain-text email is fired automatically to the Test Send Email
(or site admin email) — who accepted, which products, a direct link to the Order just
created. Always on, nothing to configure.

**Where else email content is covered** — the two automatic Reminders (unanswered offer /
outstanding balance) have their own message text and Preview/Send Test buttons under
Settings → Notifications, following the same "always-appended" pattern.

---

## Insights

### Dashboard

The **Overview** page (first submenu item; opens when clicking "Angry Offers PRO" in the
main menu) is the financial command center — a live snapshot of revenue, outstanding
money, and activity, without opening a single Order.

**Growth strip** — five figures across the top, each compared to the previous month with
a small up/down badge: Revenue, Net Profit, Cost, Avg. Order Value, Offer Conversion Rate
(% of sent offers accepted, shown as percentage-point change).

**At-a-glance stat cards** — a 2×2 block: Offers Pending, Unpaid Orders (count), Open
Balance (All Clients), Collections (current calendar year). Next to it, three "New
Customers / New Offers / New Orders" tiles, each with a small sparkline of recent activity.

**Client Offers Status** — pie chart of every Client Offer by status (in progress,
accepted, rejected) — quickest view of pipeline shape.

**Supplier snapshot** — stacked mini-summary of the purchasing side: total Supplier
Orders (count and value), how much paid to suppliers so far, how much still owed.

**Orders In Progress & Outstanding Balances** — three worklists, always live regardless of
the year filter:

- **Orders In Progress (last 10)** — most recent Orders not yet fully settled, linking to
  edit screens.
- **Outstanding Client Balances** — Orders with money still owed, oldest first, with a
  "Days" column.
- **Outstanding Supplier Balances** — same idea for what's still owed to suppliers.

**Financial Overview card** — tabbed, own **Month** filter (specific month or "All
year"), three views:

- **Income / Expenses** — recorded Payments (income) vs Supplier Payments (expenses) by
  month — real cash movement on payment date, not revenue recognition.
- **Offers / Orders** — monthly count of Client Offers vs Orders created.
- **Paid / Unpaid Orders** — monthly Order totals vs amount actually paid so far; gap =
  outstanding balance.

**Year-filtered charts** — a **Year** selector drives three charts, always comparing
selected year vs previous:

- **Revenue by Month** — this year vs last year.
- **Top Clients by Revenue** — ranked list, each client's share of total + YoY change.
- **Top Suppliers by Spend** — same ranking for purchasing.

**Quick Links** — row of shortcut buttons at the bottom: "+ New Order", direct links to
Suppliers, Clients, Supplier Offers, Client Offers, Price List, Supplier Orders, Supplier
Payments, and the full Reports page.

### Reports

A deeper, filterable breakdown than the Dashboard, per Client or per Supplier, always for
a chosen Year (and optionally a specific Quarter).

**By Client** — pick a specific Client or "All Clients", optionally narrow to one Quarter:

- Stat cards — number of Offers in the period, **Success Rate** (share Accepted),
  Revenue, VAT.
- A "Revenue by Month" chart and an "Offers Status" pie chart for the period.
- An **Orders List** table of every paid Order in the period.

**By Supplier** — mirrors the client tab, two independent blocks:

- **Payments to Suppliers (from paid Client Orders)** — with a Supplier selected: Order
  count and total payments (excl./incl. VAT) for the period, plus matching Orders table.
  With none selected: a ranking table across all suppliers.
- **Offers I've Received** — that supplier's Supplier Offers for the Year (Quarter
  filtering doesn't apply): count, Total Cost of accepted ones, Offers Status pie chart,
  full Offers List.

---

## Configuration

### Settings

Everything global to the plugin lives under Settings, split into tabs. Normally the first
thing to configure on a new install.

**General Settings** — the basics every offer/order relies on:

- **Currency Symbol** — display symbol (e.g. €) used throughout admin, PDFs, emails.
  Purely cosmetic — the ISO currency code for Stripe/PayPal is set separately under
  Payment Gateways.
- **Default VAT Rate (%)** — pre-filled on every new offer, still editable per offer.
- **Document Numbering** — independent prefix / next number / digit-count for each of:
  Offer, Order, Supplier Order, Proforma Invoice, Invoice. Useful for continuing numbering
  from a previous system; live preview of the next number as you type.
- **Custom PDF Font** — optionally upload Regular/Bold `.ttf` files to replace the default
  (Open Sans).

**Company Details** — shown at the top of every offer/proforma/invoice email: Email Logo
(via Media Library), Company Name, VAT Number, Address, Phone Numbers.

**Email Messages** — controls what surrounds the pricing table in every email:

- **General Message** — shown above the pricing table on every offer.
- **Footer Information** — shown at the bottom of every email.
- **Bank Accounts** — shown only when a Proforma Invoice is sent, just above the Footer
  (also tells the client where to transfer money for Bank Transfer).
- **Sender Email** — "from" address for outgoing offers; falls back to site admin email.
- **Test Send Email** — destination for every "Send Test" button, and "Reply-To" on real
  emails.
- **Copy** — default on/off for BCC'ing the Test Send Email, overridable per send.

**Payment Gateways & Notifications** — two tabs with their own full chapters: Online
Payments (Stripe / PayPal / WooCommerce Checkout / Bank Transfer, key validation,
currency) and Reminders & Notifications (the two automatic reminders and their timing).

**Autocomplete Dictionary** — every Product name and Description typed into a Price
List/offer line is remembered and suggested again via autocomplete. Lists everything
learned so far in two columns (Products / Descriptions), delete button per entry to clean
up typos — deleting here doesn't affect any offer/order that already used that text.

**Translations** — covered in full below. In short: a two-column table (original / your
translation) for every fixed string the plugin displays, with a master on/off switch.

**Importing from the free version** — if the free "Angry Offers Management" plugin is also
installed and still has data, an amber "Import from Free version" box appears
automatically with a record count and an import action — otherwise it doesn't show at all.

### Translations

Found under Settings → Translations — translate every fixed piece of text the plugin
displays (admin screens, menus, labels, emails, PDF documents) into your own language,
without touching code.

**How it finds the text** — no separate, hand-maintained translation file. The plugin
scans its own PHP source code (cached, refreshed automatically on plugin version change,
or manually via "Rescan Texts") for every place a fixed string is displayed, listing each
unique one as a row: Original Text on the left, empty Translation box on the right.
Untranslated text falls back to the original.

**Using the table:**

- A search box above each column filters both together (AND).
- Typing in a Translation box saves automatically on blur — no "Save" button, no reload.
- The master **Enable Translations** switch turns the whole feature on/off — off shows
  originals regardless of what's saved.

**Things worth knowing:**

- If an original text contains a placeholder like `%s` or `%d`, keep it exactly in your
  translation too — it's automatically replaced with the real value.
- Supported alphabets: Latin, Greek, Cyrillic (plus common punctuation/currency symbols) —
  no right-to-left languages or ideographic scripts (partly due to the bundled PDF font).
- HTML is stripped from input; a translation is capped at 1000 characters.
- Only strings the current scan actually recognizes can be saved — keeps the stored
  translation map from accumulating stale entries from an older plugin version.

Strings inside the admin JavaScript file (a handful of confirmation dialogs) use a
separate, small mechanism (translatable placeholders passed from PHP) since JS source
isn't part of the scan.

---

## Appendix: entity/record summary (for Laravel data modeling)

Quick cross-reference of what is a first-class record vs. a computed/frozen view, useful
when mapping this onto Eloquent models:

| Topic | Record type | Notes |
|---|---|---|
| Supplier | CRUD record | Contact + read-only computed financials per year |
| Client | CRUD record | Contact + read-only computed financials per year |
| Price List item | CRUD record | Has nested Price Tiers (1-to-many) |
| Attribute Group | CRUD record | Has Global Fields + Presets (1-to-many each) |
| Client Offer | CRUD record | Has Offer Products (lines), Order History log, Attached File |
| Order | Created only via Offer or "+ New Order" | Has Order Products (lines, snapshot + status), Payments, Product Status History |
| Proforma Invoice | **Not persisted** | Always regenerated live from the Order; no DB table needed, just a generator/service |
| Invoice | Created only by sending | Frozen snapshot record, immutable after creation |
| Payment | Created via Order or gateway webhook | Two-way synced with Order's Payments sub-table; date lock rule for confirmed online payments |
| Supplier Offer | CRUD record | Has Products (lines, no Attributes), Attached File |
| Supplier Order | Created manually | Has Products (lines), Payments |
| Supplier Payment | Created via Supplier Order only | Two-way synced with Supplier Order's Payments sub-table |
| Settings | Singleton config | Tabs: General, Company Details, Email Messages, Payment Gateways, Notifications, Autocomplete Dictionary, Translations |
| Translation string | Scanned + cached | Original text auto-discovered from source scan; translation stored keyed to original |

**Cross-cutting mechanisms to replicate:**

- One-time "copy" pickers (Price List → line, Supplier Offer → line) vs. genuine two-way
  synced sub-records (Order Payments ↔ Payment records).
- Per-line status/visibility toggles that gate what appears in outgoing emails.
- Independent per-document-type numbering (prefix/next-number/digits) with live preview.
- PDF generation as an ephemeral, request-time artifact — never stored.
- Daily scheduled job (WP-Cron here → a Laravel scheduled command) for the two reminder
  types, each anchored to a specific "sent" timestamp rather than a mutable date field.
- Online payment gateways (Stripe, PayPal, WooCommerce equivalent, Bank Transfer) as
  pluggable payment methods behind one "Pay Online" concept, with webhook confirmation for
  Stripe/PayPal and manual confirmation for Bank Transfer.
