平台

Catalogue

A catalogue shaped like the shelf, not like the database

Real stock does not arrive in tidy one-price, one-barcode units. A pharmacy buys boxes of twelve strips and sells both. A clothing shop sells the same shirt in five sizes and three colours, each with its own cost. A shop that has been trading for eleven years has a spreadsheet, not a schema. Akkad's item model is built around those facts rather than around them.

An item in Akkad is one row that knows how it is identified, how it is priced, how it is counted and how it is shown. Those four concerns are kept separate on purpose, because merchants change them at different rates — a price moves weekly, a barcode never, a photograph twice a year.

Two identifiers are enforced by the database rather than by the interface. A SKU is unique across your organisation, and so is a barcode. Item SKUs are additionally checked against variant SKUs, so a code can never mean two different things depending on which table you happen to be looking at. There is a live check endpoint behind the create form, so a clash surfaces while you are typing rather than when you press save.

01

What an item carries

Grouped by what each field is for. Everything here is per item — and, where noted, per variant as well.

GroupFieldsNotes
IdentityItem name, SKU, barcode, slug, brandSKU and barcode are unique per organisation and enforced in the database. Barcodes are capped at 32 characters.
PriceSale price, regular price, cost priceSale price is what you charge today; regular price is the compare-at figure a storefront strikes through. Cost price feeds margin, and margin-based discounting.
CountingManage stock, stock, low stock threshold, item unit, item weightManage stock is per item, so a service or a made-to-order dish can live in the same catalogue without ever being decremented. Item unit is free text and defaults to pcs.
PlacementCategory, subcategory, is published, is featuredA category is required; a subcategory is optional. Category filters match either level.
DescriptionShort description, description, main image, additional imagesOne main image plus up to nine more, reorderable. If no main image exists, the first additional upload is promoted automatically.
StructureItem type, attribute title, variant title, has pack sizes, batch trackingAttribute and variant titles are yours to rename — Colour and Size, or Flavour and Jar, or whatever your trade calls them.
Trade-specificDosage, active ingredient, is taxable, rating average, rating countDosage and active ingredient serve the pharmacy build. Ratings are denormalised from storefront reviews.

Every image uploaded anywhere in the catalogue goes through the same conversion: WebP, with a quality ladder that starts at 88 and walks down toward a floor of 55, and only then reduces dimensions, until the file lands under its size target. The point is that a merchant photographing stock on a phone in a shop should not have to think about any of this, and the storefront should still load quickly on a slow connection.

02

Three shapes an item can take

Most catalogues need all three, and mixing them freely is normal. The shape decides how stock is counted, not just how the product is displayed.

One row

Simple

One product, one price, one stock number. A bag of rice, a phone charger, a service. Pack sizes can still be layered on top of a simple item.

One axis

Variable

One product with a set of variants along a single axis — sizes, weights, capacities. Each variant carries its own price, cost, barcode and stock.

Two levels

Configurable

An attribute owns a set of values: Red owns S, M and L; Blue owns its own S, M and L. Attributes carry their own image and sort order, so the storefront can show swatches.

03

Variants, and what each one owns

A variant in Akkad is not a label attached to a parent price. It is a full commercial object, because that is how it behaves in practice — the extra-large costs more to buy and sells for more, and it has its own barcode printed on its own polybag.

Everything commercial is per variant

Image, SKU, barcode, stock, sale price, regular price, cost price — and its own pack sizes. A variant can be sold by the box while its sibling is not.

Parent stock is derived, never written

The stock shown on the parent item is the sum of its variants. There is no separate parent number to fall out of step, because there is no separate parent number at all.

Codes are unique across the whole organisation

A variant SKU or barcode collides with nothing else you own — not another variant, not another item. Duplicate combinations of item, attribute and value are refused outright.

Two hundred variants, fifty attributes

The ceilings are enforced identically on create, on update and on bulk import, so a spreadsheet cannot smuggle in a shape the interface would have refused.

Sizes sort the way people say them

Numeric values sort numerically. Clothing sizes follow a built-in ladder from XXS through 6XL, so Medium never lands between Large and Small. Everything else sorts alphabetically, and configurable items order by attribute first.

04

Sell the box and sell the single

A pharmacy buys a box of twelve strips and sells both the box and the strip. In most systems this becomes two products, and from that moment the two drift: stock is right on one and wrong on the other, a price rise lands on one and not the other, and nobody can tell you how many strips are actually in the building.

Akkad models it as one item with one stock pool, sold at more than one granularity. A pack size is a small row — name, how many base units it holds, the price of the whole pack, its own barcode, and a sort order — attached to the item or to one specific variant. Box of 12. Carton of 48. Strip.

The price is the price of the pack, set independently of the single-unit price. That is the whole point: a carton can be cheaper per unit than twelve singles, which is exactly the wholesale-to-retail spread merchants already run in their heads. And because every level carries its own barcode, scanning the carton resolves the item and pre-selects the right pack in one motion.

  • One shared stock budget. Selling one box of twelve and twelve singles against a stock of twelve is refused, because it is the same twelve.
  • On an order line, quantity is always base units; the pack count and units-per-pack are recorded alongside it, so history stays readable years later.
  • Batch costs are always per base unit, and the system converts at the pack boundary — on sales and on purchase-order receipt alike.
  • Discount rules count base pieces; shipping rules count packs. A box of twelve is twelve pieces toward a volume discount and one thing to carry toward a delivery bracket, and those are genuinely different questions.
  • Pack sizes reach the public storefront, so a shopper can add “Box of 12” to a cart and the quantity means boxes.
Shared stock pool144 base units
Single tablet strip1 unit1,000628…041
Box of 1212 units10,000628…058
Carton of 144144 units110,000628…065
You can sell one carton, or 144 singles, or twelve boxes — but not two of those at once. Every row spends the same 144 units.
One item, one stock pool, three sellable granularities — each with its own barcode and its own price.

05

Search

The scan that skips every other query

When a scanner fires, the code is almost always exact. Treating that scan as a search — running fuzzy matching, ranking, and text similarity over a catalogue of tens of thousands of rows — is how a counter starts feeling slow. Akkad probes for an exact match first, in a strict order, and on a hit every remaining query is skipped entirely.

Five probes, in order

Item barcode, then variant barcode, then pack-size barcode, then item SKU, then variant SKU. One indexed lookup. A pack-barcode hit does not just find the product — it tells the app which pack was scanned, so the box is already selected.

Typing is forgiven; scanning is not guessed at

When nothing matches exactly, a single consolidated query runs the fuzzy path, with typo rescue at word level. When a correction produced the result, the response says so, so the interface can show what it actually searched for rather than silently substituting.

Arabic-Indic and Latin digits are the same digits

٥ and 5 are folded to each other during search. In a market where the same product code is written both ways depending on who typed it, this is the difference between finding an item and re-creating it as a duplicate.

Built to stay fast as you grow

Eight composite indexes plus full-text indexes over item name, SKU and description, and over variant SKUs. Variant matching uses a correlated existence check rather than a join, so a product with sixty variants never multiplies into sixty result rows.

06

Working with a large catalogue

The operations that stop mattering at fifty items and start mattering at five thousand.

Filters that understand stock

Filter by category — matching either the category or the subcategory — by featured, by on-sale, by price range, and by stock status. Low stock means what a merchant means by it: stock tracking is on, a threshold is set, and the count is above zero but at or below that threshold.

Eight sort modes, all deterministic

Every sort carries a defined tiebreak, so paging through a list never shows you the same row twice or skips one because two items happened to share a price.

Duplicate, properly

Duplicating an item deep-copies its attributes, variants, pack sizes and images — real copies of the image files, not shared references, so editing one never changes the other. A free SKU is derived automatically. Barcode and slug are deliberately not copied, because those must stay unique, and featured status resets.

Resolve four hundred codes at once

The batch lookup endpoint takes up to 400 SKUs or barcodes per request and answers with four bulk queries rather than four hundred round trips. It is rate-limited to 25 calls a day per organisation, which is generous for reconciliation and hostile to scraping.

One main image, nine more

Additional images are reorderable, and the first one uploaded is promoted to main automatically when none exists — so an item is never left looking empty because of the order someone happened to upload in.

Check a SKU before you save

A dedicated validation call means the create form can tell you a code is taken while you are still in the field, instead of losing a half-typed item to a save error.

History that reads in the reader's language

Every item keeps a log: who did it, when, and the stock before and after, across more than twenty tracked action types — manual adjustments, order deductions and restorations, storefront sales, purchase-order receipts and reversals, batch creation and adjustment, bulk imports, and every field change from name to cost price. The entries are not stored as English sentences. They are stored as keys and variables and rendered at read time, so an owner reading in Arabic and an accountant reading in English see the same event described natively in their own language — including entries written years earlier by someone who used neither.

07

Categories that never orphan history

Categories are the axis your pricing rules, delivery rules and reports all measure against, so they are treated as structure rather than as tags.

Two levels, and real nesting

A category can carry a parent. Items hold a required category and an optional subcategory, and any category filter matches either level — so filtering by a parent still returns everything filed beneath it.

Images, through the same pipeline

Categories carry their own image, converted exactly like item images, which is what makes image-led storefront layouts possible without a separate asset workflow.

Names and slugs are policed

Slugs are validated to lowercase words joined by hyphens and are unique per organisation. Names are checked for duplicates case-insensitively, so Drinks and drinks cannot quietly coexist.

Deleting one never breaks the past

There is a system Uncategorised category that is created automatically and cannot be deleted. Deleting any other category moves its items there, clears the subcategory reference on items pointing at it, and repoints historical order lines. Last year's orders keep reporting correctly.

08

Bringing an existing catalogue in

Nobody starts from nothing. The importer is built around the sheet a merchant already has, in the state they already keep it — which mostly means being forgiving about ordering and strict about identity.

  1. 1

    Upload a spreadsheet

    XLSX or CSV, across 31 recognised columns covering identity, pricing, stock, categories, variants, pack sizes, tax, units, brand, weight, dosage and active ingredient. Creating an item requires a name and a SKU; updating one requires only the SKU.

  2. 2

    Variants come in flat

    Rows that share a base code and carry a variant value become one variable or configurable item with its variants attached. A variant row missing its base code is skipped with a reason rather than failing the file — one bad row should not cost you the other nine hundred.

  3. 3

    Pack sizes come in as continuation rows

    A pack row repeats the parent SKU, fills in the pack columns, and leaves the item name blank. The parser scans the file twice specifically so a pack row that ended up above its parent — because someone sorted the sheet by price — is still recognised. Continuation rows do not count against the row limit, which means you can round-trip your own export without being falsely rejected for being too large.

  4. 4

    Categories create themselves

    Category and subcategory cells produce the categories that do not exist yet, with the subcategory placed under its parent. There is no separate setup pass before the import.

  5. 5

    Validation is layered, not all-or-nothing

    Duplicate SKUs inside the file are a hard error, because the file itself is inconsistent and no outcome would be right. SKUs that already exist in your catalogue are per-row skips with a stated reason. A clashing pack barcode is skipped for that pack alone. An image URL column will fetch remote images, up to 100 per request. Every import writes its own history entries.

  6. 6

    Send photographs as a ZIP

    Images match to products by filename, and the code in a filename may be either a SKU or a barcode. CODE.jpg becomes the main image. CODE_1 through CODE_9 become additional images. CODE_ATTRNAME sets an attribute image on a configurable item. Up to 250 images per ZIP; macOS metadata folders are ignored; you get back matched, unmatched and error lists rather than a bare success.

09

Import and export limits

Bulk operations are throttled per organisation per day. The ceilings are set for real catalogue work and against automated abuse.

OperationPer dayFile sizePermission
Bulk create items82 MBCreate items
Bulk update items82 MBEdit items
Bulk upload images58 MBEdit items

Export goes the other way: 35 columns by default, and you choose which of them you actually want. It accepts a category filter, defaults to 1,000 rows, and adds four columns that only exist on the way out — batch name, quantity, expiry and cost. Variant, pack and batch data is only queried when you ask for it, so a plain item export stays quick. Exports also honour per-user category visibility, so a restricted team member cannot export their way around it.

10

The medicine library

The pharmacy build ships with a shared drug catalogue of 18,669 medicines. It is deliberately not scoped to your organisation — every pharmacy tenant reads the same library, and there are no write endpoints at all, so no tenant can ever pollute it for anybody else.

Every row is bilingual: an English name and an Arabic name, and search matches either. You can browse the whole catalogue, filter by name in either language, or look a barcode up directly — which means scanning the box of a product you have never stocked is a valid way to start creating it.

The columns map one-to-one onto the fields they are destined for: name, Arabic name, brand, barcode, unit, pack name, pack quantity, pack barcode, active ingredient and dosage. So importing a medicine does not create a stub to be filled in later — it creates the item and its pack size, the strip and the box, with both barcodes already attached.

Each result is stamped with whether it is already in your inventory, matched either by catalogue link or by a barcode you already carry at unit or pack level. That marker exists to stop the most annoying failure in the workflow: importing something you already stock and being rejected for a duplicate barcode after the fact. Which organisation is being checked is taken from your session and never from the request, so the marker cannot be used to probe another pharmacy's shelves.

11

Questions

Does Akkad support multiple warehouses or branches?

No. Stock is a single figure per item or variant — or per batch when batch tracking is on — scoped to one organisation. There is no location model and no stock-transfer feature. If you need per-branch stock today, Akkad is not the right fit, and we would rather say so than sell you a workaround.

Are SKUs or barcodes generated for me?

No, with one exception. Codes are yours to control, because they usually already exist on the packaging or in a supplier's system. The exception is duplicating an item, where a free SKU is derived by adding a copy suffix — and the barcode is pointedly not copied at all.

Can one variant be sold by the box while another is not?

Yes. Pack sizes attach to an item or to one specific variant, and each variant carries its own pack-size flag. A 500ml bottle can have a case of twenty-four while the 2-litre has none.

What happens to old orders if I delete a category?

Nothing breaks. Items move to the system Uncategorised category, subcategory references pointing at the deleted category are cleared, and historical order lines are repointed. Reporting on last year stays accurate.

Can I re-import a file I exported from Akkad?

Yes, and this is specifically designed for. Exported pack sizes come back as continuation rows, the importer scans the file twice so their order does not matter, and continuation rows are excluded from the row limit so a legitimate round trip is not rejected as oversized.

Is item history readable by staff who do not speak my language?

Yes. Log entries are stored as structured events rather than as sentences and are translated when they are read, so each person sees the same history in their own language.

Stock and batches

Where the numbers on these items come from — expiry-first deduction, weighted-average costing and purchase orders.

Pricing rules

Categories, pieces and margins are what the discount engine measures. This is how it measures them.

Online store

How this catalogue renders for shoppers — variants, pack sizes, categories and images on the storefront.

拿你自己的商品清单试一试

选一个合适的套餐,从表格导入商品,当天就能拥有可用的网店、收银台和订单账。

提供免费套餐。试用无需绑卡。导入会把你现有的商品清单一并带过来。