package contract import ( "time" "github.com/google/uuid" ) type CreatePurchaseOrderRequest struct { VendorID *uuid.UUID `json:"vendor_id,omitempty" validate:"omitempty"` PONumber string `json:"po_number" validate:"required,min=1,max=50"` TransactionDate string `json:"transaction_date" validate:"required"` // Format: YYYY-MM-DD DueDate *string `json:"due_date,omitempty" validate:"omitempty"` // Format: YYYY-MM-DD Reference *string `json:"reference,omitempty" validate:"omitempty,max=100"` Status *string `json:"status,omitempty" validate:"omitempty,oneof=draft sent approved received cancelled"` Message *string `json:"message,omitempty" validate:"omitempty"` TeamScope *string `json:"team_scope,omitempty" validate:"omitempty,oneof=category central"` TeamCategoryID *uuid.UUID `json:"team_category_id,omitempty" validate:"omitempty"` // CashAdvanceID marks the purchase as paid out of cash advanced to the team. Sending // it without a team charges the purchase to the cash advance's team. CashAdvanceID *uuid.UUID `json:"cash_advance_id,omitempty" validate:"omitempty"` Items []CreatePurchaseOrderItemRequest `json:"items" validate:"required,min=1,dive"` AttachmentFileIDs []uuid.UUID `json:"attachment_file_ids,omitempty"` } type CreatePurchaseOrderItemRequest struct { IngredientID *uuid.UUID `json:"ingredient_id,omitempty" validate:"omitempty"` PurchaseCategoryID uuid.UUID `json:"purchase_category_id" validate:"required"` Description *string `json:"description,omitempty" validate:"omitempty"` Quantity *float64 `json:"quantity,omitempty" validate:"omitempty,gt=0"` UnitID *uuid.UUID `json:"unit_id,omitempty" validate:"omitempty"` Amount float64 `json:"amount" validate:"required,gte=0"` } type UpdatePurchaseOrderRequest struct { VendorID *uuid.UUID `json:"vendor_id,omitempty" validate:"omitempty"` PONumber *string `json:"po_number,omitempty" validate:"omitempty,min=1,max=50"` TransactionDate *string `json:"transaction_date,omitempty" validate:"omitempty"` // Format: YYYY-MM-DD DueDate *string `json:"due_date,omitempty" validate:"omitempty"` // Format: YYYY-MM-DD Reference *string `json:"reference,omitempty" validate:"omitempty,max=100"` Status *string `json:"status,omitempty" validate:"omitempty,oneof=draft sent approved received cancelled"` Message *string `json:"message,omitempty" validate:"omitempty"` // An empty string clears the team; omitting the field leaves it untouched. TeamScope *string `json:"team_scope,omitempty" validate:"omitempty"` TeamCategoryID *uuid.UUID `json:"team_category_id,omitempty" validate:"omitempty"` // An all-zero uuid unlinks the cash advance; omitting the field leaves it untouched. CashAdvanceID *uuid.UUID `json:"cash_advance_id,omitempty" validate:"omitempty"` Items []UpdatePurchaseOrderItemRequest `json:"items,omitempty" validate:"omitempty,dive"` AttachmentFileIDs []uuid.UUID `json:"attachment_file_ids,omitempty"` } type UpdatePurchaseOrderItemRequest struct { ID *uuid.UUID `json:"id,omitempty"` // For existing items IngredientID *uuid.UUID `json:"ingredient_id,omitempty" validate:"omitempty"` PurchaseCategoryID *uuid.UUID `json:"purchase_category_id,omitempty" validate:"omitempty"` Description *string `json:"description,omitempty" validate:"omitempty"` Quantity *float64 `json:"quantity,omitempty" validate:"omitempty,gt=0"` UnitID *uuid.UUID `json:"unit_id,omitempty" validate:"omitempty"` Amount *float64 `json:"amount,omitempty" validate:"omitempty,gte=0"` } type PurchaseOrderResponse struct { ID uuid.UUID `json:"id"` OrganizationID uuid.UUID `json:"organization_id"` OutletID *uuid.UUID `json:"outlet_id"` VendorID *uuid.UUID `json:"vendor_id"` PONumber string `json:"po_number"` TransactionDate time.Time `json:"transaction_date"` DueDate *time.Time `json:"due_date"` Reference *string `json:"reference"` Status string `json:"status"` Message *string `json:"message"` TotalAmount float64 `json:"total_amount"` TeamScope *string `json:"team_scope"` TeamCategoryID *uuid.UUID `json:"team_category_id"` CashAdvanceID *uuid.UUID `json:"cash_advance_id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Team *PurchaseTeamResponse `json:"team,omitempty"` Vendor *VendorResponse `json:"vendor,omitempty"` Items []PurchaseOrderItemResponse `json:"items,omitempty"` Attachments []PurchaseOrderAttachmentResponse `json:"attachments,omitempty"` } // PurchaseTeamResponse is one entry of the team picker. Teams come from the parent // product categories; Pusat is the extra entry that has no category behind it, so // its CategoryID is null. type PurchaseTeamResponse struct { Scope string `json:"scope"` CategoryID *uuid.UUID `json:"category_id"` Name string `json:"name"` } type ListPurchaseTeamsResponse struct { Teams []PurchaseTeamResponse `json:"teams"` } type PurchaseOrderItemResponse struct { ID uuid.UUID `json:"id"` PurchaseOrderID uuid.UUID `json:"purchase_order_id"` IngredientID *uuid.UUID `json:"ingredient_id"` PurchaseCategoryID uuid.UUID `json:"purchase_category_id"` Description *string `json:"description"` Quantity *float64 `json:"quantity"` UnitID *uuid.UUID `json:"unit_id"` Amount float64 `json:"amount"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Ingredient *IngredientResponse `json:"ingredient,omitempty"` PurchaseCategory *PurchaseCategoryResponse `json:"purchase_category,omitempty"` Unit *UnitResponse `json:"unit,omitempty"` } type PurchaseOrderAttachmentResponse struct { ID uuid.UUID `json:"id"` PurchaseOrderID uuid.UUID `json:"purchase_order_id"` FileID uuid.UUID `json:"file_id"` CreatedAt time.Time `json:"created_at"` File *FileResponse `json:"file,omitempty"` } type ListPurchaseOrdersRequest struct { Page int `json:"page" validate:"min=1"` Limit int `json:"limit" validate:"min=1,max=100"` Search string `json:"search,omitempty"` Status string `json:"status,omitempty" validate:"omitempty,oneof=draft sent approved received cancelled"` VendorID *uuid.UUID `json:"vendor_id,omitempty"` // Team is the single-value form of the two filters below, so the team picker // can send back what it was given: a parent category id, "central" for Pusat, // or "none" for purchases with no team yet. It replaces them rather than // narrowing alongside them. Team string `json:"team,omitempty"` TeamScope string `json:"team_scope,omitempty" validate:"omitempty,oneof=category central"` TeamCategoryID *uuid.UUID `json:"team_category_id,omitempty"` StartDate *time.Time `json:"start_date,omitempty"` EndDate *time.Time `json:"end_date,omitempty"` } type ListPurchaseOrdersResponse struct { PurchaseOrders []PurchaseOrderResponse `json:"purchase_orders"` TotalCount int `json:"total_count"` Page int `json:"page"` Limit int `json:"limit"` TotalPages int `json:"total_pages"` } // Helper types for ingredient and unit responses type IngredientResponse struct { ID uuid.UUID `json:"id"` Name string `json:"name"` } type UnitResponse struct { ID uuid.UUID `json:"id"` Name string `json:"name"` }