subcategoryid is no longer required to import a catalogue product — it's a display/grouping hint elsewhere (the codebase already has an "Uncategorized" fallback for subcategoryid=0), so requiring it was pure friction with no correctness payoff. Fixes the category picker at the root: categoryid 2, which tenant 1135's real products actually use, has no row in productcategories at all (not a filter bug — the master data is genuinely missing it). Rather than inventing category master data, adds GET /products/gettenantcategories, which lists categories a tenant's own products actually use (falling back to a synthesized label when the master table has no name), so the import category picker always offers something real instead of an incomplete global list. Also relaxes the subcategory lookup's tenant filter to include unowned/global rows (tenantid NULL or 0), not just exact tenant matches — categoryid 2's real subcategories carry no tenant at all, so the strict filter was hiding them even when a tenant wanted one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
321 lines
14 KiB
Go
321 lines
14 KiB
Go
package models
|
|
|
|
import "time"
|
|
|
|
type ProductSubCategory struct {
|
|
Subcatid int `json:"subcatid"`
|
|
Categoryid int `json:"categoryid,omitempty"`
|
|
Tenantid int `json:"tenantid,omitempty"`
|
|
Subcatname string `json:"subcatname,omitempty"`
|
|
Image string `json:"image,omitempty"`
|
|
Status string `json:"status,omitempty"`
|
|
Sortorder int `json:"sortorder,omitempty"`
|
|
Createdby int `json:"createdby,omitempty"`
|
|
Created time.Time `json:"created,omitempty"`
|
|
Updated time.Time `json:"updated,omitempty"`
|
|
}
|
|
|
|
type Productcount struct {
|
|
Total int `json:"total"`
|
|
Available int `json:"available"`
|
|
Outofstock int `json:"outofstock"`
|
|
}
|
|
|
|
type ProductCategory struct {
|
|
Categoryid int `json:"categoryid"`
|
|
Moduleid int `json:"moduleid"`
|
|
Tenantid int `json:"tenantid,omitempty"`
|
|
Categorytypeid int `json:"categorytypeid,omitempty"`
|
|
Categoryname string `json:"categoryname,omitempty"`
|
|
Image string `json:"image,omitempty"`
|
|
Catalougecategoryid int `json:"catalougecategoryid,omitempty"`
|
|
Sortorder int `json:"sortorder,omitempty"`
|
|
Status string `json:"status"`
|
|
Createdby int `json:"createdby,omitempty"`
|
|
Created time.Time `json:"created"`
|
|
Updated time.Time `json:"updated"`
|
|
}
|
|
|
|
type Productvariant struct {
|
|
Variantid int `json:"variantid" gorm:"Primary_Key"`
|
|
Tenantid int `json:"tenantid"`
|
|
Variantname string `json:"variantname"`
|
|
Categoryid int `json:"categoryid" gorm:"default:0"`
|
|
Categoryname string `json:"categoryname" gorm:"-"`
|
|
Subcategoryid int `json:"subcategoryid"`
|
|
Status string `json:"status" gorm:"default:active"`
|
|
}
|
|
|
|
type Products struct {
|
|
Productid int `json:"productid" gorm:"primaryKey;autoIncrement"`
|
|
AppLocationid int `json:"applocationid" gorm:"column:applocationid"`
|
|
Productlocationid int `json:"productlocationid" gorm:"->"`
|
|
Tenantid int `json:"tenantid,omitempty"`
|
|
Categoryid int `json:"categoryid"`
|
|
Categoryname string `json:"categoryname" gorm:"->"`
|
|
Subcategoryid int `json:"subcategoryid,omitempty"`
|
|
Subcategoryname string `json:"Subcategoryname" gorm:"->"`
|
|
Catalogueid int `json:"catalogueid,omitempty"`
|
|
Addonid int `json:"addonid,omitempty"`
|
|
Discountid int `json:"discountid"`
|
|
Discountvalue float64 `json:"discountvalue"`
|
|
Pricingid int `json:"pricingid,omitempty"`
|
|
Productname string `json:"productname,omitempty"`
|
|
Productimage string `json:"productimage,omitempty"`
|
|
Productdesc string `json:"productdesc,omitempty"`
|
|
Productsku string `json:"productsku,omitempty"`
|
|
Brandid int `json:"brandid,omitempty"`
|
|
Productbrand string `json:"productbrand,omitempty"`
|
|
Productunit string `json:"productunit,omitempty"`
|
|
Unitvalue string `json:"unitvalue,omitempty"`
|
|
Toppicks string `json:"toppicks,omitempty"`
|
|
Productcost float64 `json:"productcost,omitempty"`
|
|
Taxamount float64 `json:"taxamount,omitempty"`
|
|
Taxpercent float64 `json:"taxpercent,omitempty"`
|
|
Producttax int `json:"producttax" gorm:"default:0"`
|
|
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"`
|
|
Retailprice float64 `json:"retailprice,omitempty"`
|
|
Diffprice float64 `json:"diffprice,omitempty"`
|
|
Diffpercent float64 `json:"diffpercent,omitempty"`
|
|
Othercost float64 `json:"othercost,omitempty"`
|
|
Approve int `json:"approve"`
|
|
Productstatus string `json:"productstatus" `
|
|
// Status string `json:"status" gorm:"default:InActive"`
|
|
// Status string `json:"status" gorm:"-"`
|
|
}
|
|
|
|
type Locationproducts struct {
|
|
Productid int `json:"productid"`
|
|
AppLocationid int `json:"applocationid" gorm:"column:applocationid"`
|
|
Productlocationid int `json:"productlocationid" gorm:"->"`
|
|
Tenantid int `json:"tenantid,omitempty"`
|
|
Categoryid int `json:"categoryid"`
|
|
Categoryname string `json:"categoryname" gorm:"->"`
|
|
Subcategoryid int `json:"subcategoryid,omitempty"`
|
|
Subcategoryname string `json:"Subcategoryname" gorm:"->"`
|
|
Catalogueid int `json:"catalogueid,omitempty"`
|
|
Addonid int `json:"addonid,omitempty"`
|
|
Discountid int `json:"discountid,omitempty"`
|
|
Pricingid int `json:"pricingid,omitempty"`
|
|
Productname string `json:"productname,omitempty"`
|
|
Productimage string `json:"productimage,omitempty"`
|
|
Productdesc string `json:"productdesc,omitempty"`
|
|
Productsku string `json:"productsku,omitempty"`
|
|
Brandid int `json:"brandid,omitempty"`
|
|
Productbrand string `json:"productbrand,omitempty"`
|
|
Productunit string `json:"productunit,omitempty"`
|
|
Unitvalue string `json:"unitvalue,omitempty"`
|
|
Toppicks string `json:"toppicks,omitempty"`
|
|
Productcost float64 `json:"productcost,omitempty"`
|
|
Taxamount float64 `json:"taxamount,omitempty"`
|
|
Taxpercent float64 `json:"taxpercent,omitempty"`
|
|
Producttax int `json:"producttax" gorm:"default:0"`
|
|
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"`
|
|
Retailprice float64 `json:"retailprice,omitempty"`
|
|
Diffprice float64 `json:"diffprice,omitempty"`
|
|
Diffpercent float64 `json:"diffpercent,omitempty"`
|
|
Othercost float64 `json:"othercost,omitempty"`
|
|
Approve int `json:"approve" gorm:"default:0"`
|
|
// Productstatus string `json:"productstatus" gorm:"default:available"`
|
|
Status string `json:"status" gorm:"default:outofstock"`
|
|
}
|
|
|
|
type Productstocks struct {
|
|
Productstockid int `json:"productstockid" gorm:"Primary_Key"`
|
|
Locationid int `json:"locationid"`
|
|
Tenantid int `json:"tenantid"`
|
|
Stockdate string `json:"stockdate"`
|
|
Productid int `json:"productid"`
|
|
Quantity int `json:"quantity"`
|
|
Stocktype string `json:"stocktype"`
|
|
Status string `json:"status"`
|
|
AppLocationid int `json:"applocationid"`
|
|
Categoryid int `json:"categoryid"`
|
|
Categoryname string `json:"categoryname" gorm:"->"`
|
|
Subcategoryid int `json:"subcategoryid,omitempty"`
|
|
Subcategoryname string `json:"Subcategoryname" `
|
|
Productname string `json:"productname,omitempty"`
|
|
Productimage string `json:"productimage,omitempty"`
|
|
Productdesc *string `json:"productdesc,omitempty"`
|
|
Productsku *string `json:"productsku,omitempty"`
|
|
Brandid *int `json:"brandid,omitempty"`
|
|
Productbrand *string `json:"productbrand,omitempty"`
|
|
Productunit *string `json:"productunit,omitempty"`
|
|
Unitvalue *string `json:"unitvalue,omitempty"`
|
|
Toppicks *string `json:"toppicks,omitempty"`
|
|
Productcost *float64 `json:"productcost,omitempty"`
|
|
Taxamount *float64 `json:"taxamount,omitempty"`
|
|
Taxpercent *float64 `json:"taxpercent,omitempty"`
|
|
Producttax *int `json:"producttax,omitempty"`
|
|
Productstock *int `json:"productstock,omitempty"`
|
|
Productcombo *int `json:"productcombo,omitempty"`
|
|
Variants int `json:"variants"`
|
|
Retailprice float64 `json:"retailprice,omitempty"`
|
|
Diffprice float64 `json:"diffprice,omitempty"`
|
|
Diffpercent float64 `json:"diffpercent,omitempty"`
|
|
Othercost float64 `json:"othercost,omitempty"`
|
|
Approve *int `json:"approve"`
|
|
}
|
|
|
|
type Productstock struct {
|
|
Productstockid int `json:"productstockid" gorm:"Primary_Key"`
|
|
Tenantid int `json:"tenantid"`
|
|
Stockdate time.Time `json:"stockdate"`
|
|
Locationid int `json:"locationid"`
|
|
Productid int `json:"productid"`
|
|
Quantity int `json:"quantity"`
|
|
Stocktype string `json:"stocktype"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
type Productstockstatement struct {
|
|
Productid int `json:"productid"`
|
|
Productname string `json:"productname"`
|
|
Productimage string `json:"productimage"`
|
|
Categoryid int `json:"categoryid"`
|
|
Subcategoryid int `json:"subcategoryid"`
|
|
Productunit string `json:"productunit"`
|
|
Unitvalue string `json:"unitvalue"`
|
|
Productcost float32 `json:"productcost"`
|
|
Taxpercent float32 `json:"taxpercent"`
|
|
Taxamount float32 `json:"taxamount"`
|
|
Retailprice float32 `json:"retailprice"`
|
|
Tenantid int `json:"tenantid"`
|
|
Locationid int `json:"locationid"`
|
|
Opening int `json:"opening"`
|
|
Credit int `json:"credit"`
|
|
Debit int `json:"debit"`
|
|
Closing int `json:"closing"`
|
|
}
|
|
|
|
type ProductSummary struct {
|
|
Subcategoryid int `json:"subcategoryid"`
|
|
Subcategroyname string `json:"subcategroyname"`
|
|
Image string `json:"image"`
|
|
Productcount int `json:"productcount"`
|
|
}
|
|
|
|
type Tenantproducts struct {
|
|
Tenant TenantInfo `json:"tenant"`
|
|
Products []Products `json:"products"`
|
|
// Locationproducts []Locationproducts `json:"locationproducts"`
|
|
}
|
|
|
|
type TenantInfo struct {
|
|
Tenantid int `json:"tenantid"`
|
|
Tenantname string `json:"tenantname"`
|
|
Userfcmtoken string `json:"userfcmtoken"`
|
|
Address string `json:"address"`
|
|
Licenseno string `json:"licenseno"`
|
|
Primaryemail string `json:"primaryemail"`
|
|
Primarycontact string `json:"primarycontact"`
|
|
Pickuplocationid int `json:"pickuplocationid"`
|
|
Applocationid int `json:"applocationid"`
|
|
Suburb string `json:"suburb"`
|
|
City string `json:"city"`
|
|
Latitude string `json:"latitude"`
|
|
Longitude string `json:"longitude"`
|
|
Postcode string `json:"postcode"`
|
|
Tenantimage string `json:"tenantimage"`
|
|
Locationid int `json:"locationid"`
|
|
Locationname string `json:"locationname"`
|
|
Subcategoryid int `json:"subcategoryid"`
|
|
Categoryid int `json:"categoryid"`
|
|
Registrationno string `json:"registrationno"`
|
|
Orderscount int `json:"orderscount"`
|
|
// Products []Products `json:"products" gorm:"-"`
|
|
ProductSubcategory []ProductSubcategory `json:"productsubcategory" gorm:"-"`
|
|
}
|
|
|
|
type SubcategoryProductResponse struct {
|
|
SubcategoryID int `json:"subcategoryid"`
|
|
SubcategoryName string `json:"subcategoryname"`
|
|
Image string `json:"image"`
|
|
Products []Products `json:"products"`
|
|
}
|
|
|
|
type ProductFilter struct {
|
|
CategoryID int
|
|
TenantID int
|
|
AppLocationID int
|
|
ProductID int
|
|
Keyword string
|
|
LocationID int
|
|
}
|
|
|
|
type Subcategory struct {
|
|
Subcategoryid int `json:"subcategoryid" gorm:"column:subcatid"`
|
|
Subcategoryname string `json:"subcategoryname" gorm:"column:subcatname"`
|
|
Categoryid int `json:"categoryid" gorm:"column:categoryid"`
|
|
Image string `json:"image" gorm:"column:image"`
|
|
}
|
|
|
|
// TenantCategory is a categoryid actually in use by a tenant's own products,
|
|
// with a best-effort name. Used instead of the global productcategories list
|
|
// for the import category picker, since that master table is missing rows
|
|
// for categoryids that are nonetheless in real use (e.g. categoryid 2).
|
|
type TenantCategory struct {
|
|
Categoryid int `json:"categoryid"`
|
|
Categoryname string `json:"categoryname"`
|
|
}
|
|
|
|
// ImportedCatalogueRef identifies a catalogue product a tenant has already
|
|
// imported. Brand is always included, even when a caller filtered by a
|
|
// single brand, because a bare catalogueid is ambiguous across brand tables.
|
|
type ImportedCatalogueRef struct {
|
|
Brand string `json:"brand"`
|
|
Catalogueid int64 `json:"catalogueid"`
|
|
}
|
|
|
|
// ImportCatalogueProductRequest is the payload for importing a product from
|
|
// the global catalogue (CatalogueDB) into a tenant's own store catalogue.
|
|
// Brand+Catalogueid is the bridge key back to CatalogueDB: a catalogue row's
|
|
// bare id is only unique within its brand table, so both are required.
|
|
type ImportCatalogueProductRequest struct {
|
|
Tenantid int `json:"tenantid"`
|
|
Locationid int `json:"locationid"`
|
|
Brand string `json:"brand"`
|
|
Catalogueid int64 `json:"catalogueid"`
|
|
Categoryid int `json:"categoryid"`
|
|
Subcategoryid int `json:"subcategoryid"`
|
|
Quantity int `json:"quantity"`
|
|
Stocktype string `json:"stocktype"`
|
|
Status string `json:"status"`
|
|
Retailprice float64 `json:"retailprice"`
|
|
Productcost float64 `json:"productcost"`
|
|
Taxpercent float64 `json:"taxpercent"`
|
|
}
|
|
|
|
type Productlocations struct {
|
|
Productlocationid int `json:"productlocationid" gorm:"Primary_Key"`
|
|
Tenantid int `json:"tenantid"`
|
|
Locationid int `json:"locationid"`
|
|
Productid int `json:"productid"`
|
|
Catlougeid int `json:"catlougeid"`
|
|
Minquantity int `json:"minquantity" gorm:"default:0"`
|
|
Maxquantity int `json:"maxquantity" gorm:"default:0"`
|
|
Price float32 `json:"price" gorm:"default:0.0"`
|
|
Quantity int `json:"quantity" gorm:"<-:false"`
|
|
Stocktype string `json:"stocktype" gorm:"<-:false"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
type ProductSubcategory struct {
|
|
Subcatid int `json:"subcatid"`
|
|
Categoryid int `json:"categoryid"`
|
|
Tenantid int `json:"tenantid"`
|
|
Subcatname string `json:"subcatname"`
|
|
Status string `json:"status"`
|
|
Sortorder int `json:"sortorder"`
|
|
Createdby int `json:"createdby"`
|
|
Created time.Time `json:"created"`
|
|
Updated time.Time `json:"updated"`
|
|
Image string `json:"image"`
|
|
}
|