product variant id

This commit is contained in:
2026-09-02 13:11:01 +05:30
parent 9f9b05ed1c
commit 68871cb043
8 changed files with 450 additions and 5 deletions

View File

@@ -36,14 +36,48 @@ type ProductCategory struct {
Updated time.Time `json:"updated"`
}
// One size of one product — "500ml" hanging under the parent "Coke".
//
// The table always had the two columns that make this a relationship rather
// than a list of words: `productid` names the PARENT, `variantproductid` names
// the product a shopper actually orders when they pick this size. Neither was
// mapped here, so nothing could read or write them: `createproductvariant`
// wrote rows with no parent, and `getproductvariants` returned a flat
// per-tenant list of names. Measured 2026-09-02 across ten tenants: two rows
// existed on the whole platform, named "testing" and "demo", attached to
// nothing.
//
// The consequence is the ordering bug. With no way to attach a variant to a
// product, the app had only `products.variants` — a bare group number, 0 on
// every product — to go on.
type Productvariant struct {
Variantid int `json:"variantid" gorm:"Primary_Key"`
Tenantid int `json:"tenantid"`
Variantname string `json:"variantname"`
Variantid int `json:"variantid" gorm:"primaryKey;autoIncrement"`
Tenantid int `json:"tenantid"`
// The parent. A variant is meaningless without one, so this is what
// AddProductVariant refuses to accept as zero.
Productid int `json:"productid"`
// The product to actually put in the basket for this size. It is a real
// product row, so it carries its own price, stock and barcode — which is
// why a variant does not need to duplicate any of them.
Variantproductid int `json:"variantproductid"`
Variantname string `json:"variantname"`
Varianttype string `json:"varianttype,omitempty"`
Price float64 `json:"price,omitempty"`
Categoryid int `json:"categoryid" gorm:"default:0"`
Categoryname string `json:"categoryname" gorm:"-"`
Subcategoryid int `json:"subcategoryid"`
Status string `json:"status" gorm:"default:active"`
Status string `json:"status" gorm:"default:Active"`
// Read-only, from the variant's own product row. The app needs a name and a
// price to draw a size picker; without these it would have to fetch each
// variant separately to render one screen.
Variantproductname string `json:"variantproductname" gorm:"->"`
Variantprice float64 `json:"variantprice" gorm:"->"`
Variantstock int `json:"variantstock" gorm:"->"`
}
type Products struct {
@@ -107,7 +141,20 @@ type Products struct {
Productstock int `json:"productstock" gorm:"default:0"`
Productcombo int `json:"productcombo" gorm:"default:0"`
Variants int `json:"variants" gorm:"default:0"`
Quantity int `json:"quantity"`
// The sizes hanging under this product, so one call draws the whole screen.
//
// A separate field from `Variants` above, which is the legacy group NUMBER
// and stays exactly as it was — renaming it would break every caller, and it
// is still what an old client reads. This is the list.
//
// Always present, and EMPTY for a product with no sizes. That is the case
// that matters: an empty list is a complete answer meaning "order this one
// directly", which is what lets the app proceed instead of stalling on a
// choice that does not exist.
Variantoptions []Productvariant `json:"variantoptions" gorm:"-"`
Quantity int `json:"quantity"`
// Price is the EFFECTIVE selling price at the location a query was scoped
// to: productlocations.price when the store has set one, otherwise the
// master Retailprice below. Read-only — it is computed by the query, never