# Manage Image Size & Price (PhotoPrint)

- **Module:** `Brushyourideas_Photoprint`
- **Admin path:** Product Designer → PhotoPrint → Manage Image Size & Price
- **Branch / MR:** `feature/photo-print`
- **Date:** 2026-07-09
- **Author:** vishal.parmar

## Purpose

Gives admins a single, controlled place to define the print sizes offered for
PhotoPrint products and the price of each. The configuration is a **single record**
(status + measurement unit) that owns an ordered list of size/price rows. There is
intentionally no "add another configuration" — the grid exists only to edit the one
record.

## How it works

- **Listing grid** (`photoprint/config/index`) shows exactly one row with an **Edit**
  action. The UI component deliberately omits the `<listingToolbar>`, which removes
  Add New, mass actions, filters, search, bookmarks, column controls and export in
  one stroke — leaving just the editable record.
- **Edit form** (`photoprint/config/edit`) has a **General** section (Status,
  Unit) and a **dynamic-rows** grid of size entries. Each row has Image Width,
  Image Height, Price, Position, Status and a trash-icon delete (`actionDelete`).
  Rows are validated (numeric width/height/price, integer position) and ordered by
  Position.
- **Save** (`photoprint/config/save`) runs inside a **DB transaction**: it updates
  the single config record, then syncs the child rows — inserts new ones, updates
  existing (matched by hidden `size_id`), and deletes rows removed in the form
  (via the `delete` flag plus an id-diff safety net). It never creates a second
  config record.
- **Load** (`Model/Config/DataProvider`) reads the config and its child rows ordered
  by position, trimming trailing decimal zeros (`10.0000` → `10`) for display.

## Data model

| Table | Key columns | Notes |
|-------|-------------|-------|
| `productdesigner_photoprint_config` | `config_id` (PK), `status`, `unit`, `created_at`, `updated_at` | The single configuration record |
| `productdesigner_photoprint_size` | `size_id` (PK), `config_id` (FK), `image_width`, `image_height`, `price`, `position`, `status` | Child rows; FK → config with `ON DELETE CASCADE` |

Width/height/price are `decimal(12,4)`. A seed data patch (`Setup/Patch/Data/DefaultConfig`)
inserts the single config record plus three starter size rows when the table is empty.

## Configuration

| Setting | Location | Values | Default |
|---------|----------|--------|---------|
| Status | Edit form → General | Enable / Disable | Enable |
| Unit | Edit form → General | Centimeter (`cm`) / Inch (`in`) | Centimeter |
| Size rows | Edit form → dynamic rows | width, height, price, position, status | 3 seeded rows |

## Integration points

- Sits under the existing **Product Designer** admin menu
  (`Printxpand_Core::sample`); the module `sequence`s `Brushyourideas_Productdesigner`.
- The size/price data is consumed by the PhotoPrint product flow (see the
  [PhotoPrint frontend feature](photoprint-frontend-feature.md), which fetches
  server-side sizes with prices).
- The dynamic-rows grid uses Magento's stock `Magento_Ui/js/dynamic-rows/dynamic-rows`
  component with the default template so inline add/delete render immediately.

## Testing

- [x] `php bin/magento setup:upgrade` — creates both tables and seeds the record
- [x] `php bin/magento setup:di:compile`
- [x] Manual: edit form loads existing rows in position order; add / edit / delete
      rows; Save persists insert/update/delete transactionally to the child table
- [x] Grid shows exactly one record with Edit only (no add/delete/filters/export)

## Security / operational notes

- **ACL:** `Brushyourideas_Photoprint::imagesize` (under `Printxpand_Core::sample` →
  `Brushyourideas_Photoprint::core`) gates the controllers and menu.
- **Seed:** the single record is created by the `DefaultConfig` data patch. If the
  grid is empty after upgrade because the patch was previously recorded as applied,
  remove its `patch_list` row and re-run `setup:upgrade` (guarded to insert only when
  the table is empty).
- **Deploy note (silkprint stack):** nginx serves `pub/static` directly with no
  on-demand fallback — do not clear `pub/static`; for UI-component XML changes,
  `cache:flush` suffices.
