# This is a production manifest.yml file for a manifest backend. # Read more about the manifest format here: https:/manifest.build/docs name: Consignment/Reseller Management System 🛍️ entities: BusinessUnit 🏢: properties: - { name: name, type: string, validation: { required: true }, helpText: "Legal name of the business unit" } - { name: brn, type: string, helpText: "Business Registration Number for official documentation" } # Contact Details fields (direct fields instead of group) - { name: email, type: email, helpText: "Business email address for communications" } - { name: phoneNumber, type: string, helpText: "Business contact phone number" } - { name: address, type: text, helpText: "Business address including street, city, and postal code" } # Financial and operational fields - { name: participationFee, type: money, options: { currency: "MUR" }, default: 0, helpText: "Fee charged to resellers for participating in editions (in MUR)" } - { name: resellerCommission, type: number, default: 50, validation: { min: 0, max: 100 }, helpText: "Percentage commission for resellers (0-100%). Business keeps the remainder." } - { name: taxRate, type: number, default: 0, validation: { min: 0, max: 100 }, helpText: "Tax rate applicable to transactions (0-100%)" } - { name: isActive, type: boolean, default: true, helpText: "Whether this business unit is currently active" } - { name: termsAndConditions, type: text, helpText: "Terms and conditions text for reseller agreements" } - { name: contractTemplate, type: text, helpText: "Template used for generating reseller contracts" } - { name: exchangeValidityDays, type: number, default: 7, helpText: "Number of days an exchange remains valid after purchase" } # No validation middleware needed - single commission field Reseller 👨‍💼: properties: # Personal Information fields - { name: firstName, type: string, validation: { required: true }, helpText: "First name" } - { name: lastName, type: string, validation: { required: true }, helpText: "Last name" } - { name: dateOfBirth, type: date, helpText: "Date of birth" } # Contact Details fields - { name: email, type: email, helpText: "Email address for communications" } - { name: phoneNumber, type: string, helpText: "Primary contact phone number" } - { name: address, type: text, helpText: "Complete address including street, city, and postal code" } # Bank Details fields - { name: bankName, type: string, helpText: "Name of the bank" } - { name: bankAccountNumber, type: string, helpText: "Bank account number" } - { name: bankAccountHolder, type: string, helpText: "Name on the bank account" } # Other fields - { name: prefix, type: string, helpText: "Auto-generated unique prefix for SKU generation" } - { name: isActive, type: boolean, default: true, helpText: "Whether this account is currently active" } - { name: notes, type: text, helpText: "Internal notes" } middlewares: beforeCreate: - handler: generatePrefix Edition 📅: properties: - { name: name, type: string, validation: { required: true }, helpText: "Name of the sales edition or event" } - { name: description, type: text, helpText: "Detailed description of the edition" } - { name: startDate, type: date, validation: { required: true }, helpText: "Date when the edition begins" } - { name: endDate, type: date, validation: { required: true }, helpText: "Original end date of the edition" } - { name: extendedEndDate, type: date, helpText: "Extended end date if edition period is prolonged" } - { name: status, type: choice, options: { values: ["upcoming", "active", "extended", "completed", "reconciled"] }, default: "upcoming", helpText: "Current status of the edition" } - { name: itemOwnership, type: choice, options: { values: ["reseller", "business_on_close"] }, default: "reseller", helpText: "Who owns unsold items after edition closes" } belongsTo: - { name: BusinessUnit, entity: BusinessUnit } Participation 🤝: properties: - { name: expectedDropDate, type: date, helpText: "Scheduled date for item drop-off" } - { name: droppedDate, type: date, helpText: "Actual date when items were dropped off" } - { name: dropStatus, type: choice, options: { values: ["scheduled", "dropped", "pending"] }, default: "scheduled", helpText: "Current status of item drop-off" } - { name: signatures, type: group, options: { group: SignatureDetails, multiple: false }, helpText: "Signature details for drop-off confirmation" } - { name: preSelectedItems, type: number, helpText: "Number of items pre-selected for this participation" } - { name: itemFate, type: choice, options: { values: ["donate", "collect"] }, helpText: "What happens to unsold items - donate or collect back" } - { name: termsAccepted, type: boolean, default: false, helpText: "Whether reseller has accepted terms and conditions" } - { name: sortDate, type: date, helpText: "Date when items were sorted and catalogued" } - { name: contractSentStatus, type: boolean, default: false, helpText: "Whether contract has been sent to reseller" } - { name: contractSentDate, type: date, helpText: "Date when contract was sent" } - { name: contractApprovalStatus, type: boolean, default: false, helpText: "Whether reseller has approved the contract" } - { name: contractApprovalDate, type: date, helpText: "Date when contract was approved" } - { name: checkoutSentStatus, type: boolean, default: false, helpText: "Whether checkout report has been sent" } - { name: checkoutSentDate, type: date, helpText: "Date when checkout report was sent" } - { name: paymentDetails, type: group, options: { group: PaymentDetails, multiple: false }, helpText: "Payment information for this participation" } - { name: notes, type: text, helpText: "Additional notes about this participation" } belongsTo: - { name: Reseller, entity: Reseller } - { name: BusinessUnit, entity: BusinessUnit } - { name: Edition, entity: Edition } middlewares: afterUpdate: - handler: calculatePayout - handler: generateParticipationSkus Product 📦: properties: - { name: name, type: string, validation: { required: true }, helpText: "Product name or title" } - { name: description, type: text, helpText: "Detailed product description" } - { name: type, type: string, validation: { required: true }, helpText: "Product category or type" } - { name: brand, type: string, helpText: "Brand or manufacturer name" } - { name: size, type: string, helpText: "Size of the product (S, M, L, XL, etc.)" } - { name: color, type: string, helpText: "Primary color of the product" } - { name: originalQuantity, type: number, default: 1, helpText: "Initial quantity when product was added" } - { name: quantity, type: number, default: 1, helpText: "Current available quantity" } - { name: soldQuantity, type: number, default: 0, helpText: "Number of units sold" } - { name: sellingPrice, type: money, options: { currency: "MUR" }, validation: { required: true }, helpText: "Retail selling price in MUR" } - { name: photo, type: image, options: { sizes: { thumbnail: { width: 100, height: 100 }, medium: { width: 400, height: 400 }, large: { width: 1024, height: 1024 } } }, helpText: "Product photo (multiple sizes generated automatically)" } - { name: status, type: choice, options: { values: ["available", "sold", "returned", "lost", "business_property"] }, default: "available", helpText: "Current status of the product" } - { name: sku, type: string, helpText: "Auto-generated Stock Keeping Unit code" } - { name: qrCode, type: text, helpText: "Auto-generated QR code data for product scanning" } - { name: barcodePrinted, type: boolean, default: false, helpText: "Whether barcode label has been printed" } - { name: itemTagged, type: boolean, default: false, helpText: "Whether physical tag has been attached" } - { name: notes, type: text, helpText: "Additional notes about this product" } - { name: timestamp, type: string, helpText: "Timestamp for tracking purposes" } belongsTo: - { name: Participation, entity: Participation } middlewares: afterCreate: - handler: generateSku Transaction 💰: properties: - { name: transactionNumber, type: string, helpText: "Auto-generated unique transaction number" } - { name: date, type: timestamp, default: "now", helpText: "Transaction date and time" } - { name: status, type: choice, options: { values: ["pending", "completed", "voided", "refunded"] }, default: "completed", helpText: "Current transaction status" } - { name: subtotal, type: money, options: { currency: "MUR" }, validation: { required: true }, helpText: "Subtotal before tax and discounts" } - { name: taxAmount, type: money, options: { currency: "MUR" }, default: 0, helpText: "Tax amount calculated based on business unit tax rate" } - { name: discountAmount, type: money, options: { currency: "MUR" }, default: 0, helpText: "Total discount amount applied" } - { name: totalAmount, type: money, options: { currency: "MUR" }, validation: { required: true }, helpText: "Final amount after tax and discounts" } - { name: amountTendered, type: money, options: { currency: "MUR" }, default: 0, helpText: "Amount paid by customer" } - { name: changeDue, type: money, options: { currency: "MUR" }, default: 0, helpText: "Change to be returned to customer" } - { name: receiptPrinted, type: boolean, default: false, helpText: "Whether physical receipt was printed" } - { name: receiptEmailed, type: boolean, default: false, helpText: "Whether receipt was emailed to customer" } - { name: receiptEmail, type: email, helpText: "Email address where receipt was sent" } - { name: notes, type: text, helpText: "Internal notes about this transaction" } belongsTo: - { name: Customer, entity: Customer } - { name: BusinessUnit, entity: BusinessUnit } middlewares: beforeCreate: - handler: generateTransactionNumber afterCreate: - handler: processTransactionItems TransactionItem 📋: properties: - { name: quantity, type: number, default: 1, helpText: "Quantity of this product in the transaction" } - { name: unitPrice, type: money, options: { currency: "MUR" }, validation: { required: true }, helpText: "Price per unit at time of sale" } - { name: discountAmount, type: money, options: { currency: "MUR" }, default: 0, helpText: "Discount applied to this line item" } - { name: totalPrice, type: money, options: { currency: "MUR" }, validation: { required: true }, helpText: "Total price for this line item (quantity × unit price - discount)" } - { name: exchangeType, type: choice, options: { values: ["original", "exchanged_return", "exchanged_receive"] }, helpText: "Type of exchange transaction for this item" } belongsTo: - { name: Product, entity: Product } - { name: Transaction, entity: Transaction } - { name: Exchange, entity: Exchange } Customer 👤: properties: # Personal Information fields - { name: firstName, type: string, validation: { required: true }, helpText: "First name" } - { name: lastName, type: string, validation: { required: true }, helpText: "Last name" } - { name: dateOfBirth, type: date, helpText: "Date of birth" } # Contact Details fields - { name: email, type: email, helpText: "Email address for communications" } - { name: phoneNumber, type: string, helpText: "Primary contact phone number" } - { name: address, type: text, helpText: "Complete address including street, city, and postal code" } # Other fields - { name: loyaltyPoints, type: number, default: 0, helpText: "Accumulated loyalty points" } - { name: isActive, type: boolean, default: true, helpText: "Whether this customer account is active" } - { name: notes, type: text, helpText: "Internal notes about this customer" } belongsToMany: - { name: DiscountRule, entity: DiscountRule } Payment 💳: properties: - { name: amount, type: money, options: { currency: "MUR" }, validation: { required: true }, helpText: "Payment amount in MUR" } - { name: reference, type: string, helpText: "Payment reference number (for card/bank transfers)" } - { name: status, type: choice, options: { values: ["completed", "failed", "refunded"] }, default: "completed", helpText: "Current payment status" } - { name: processedAt, type: timestamp, default: "now", helpText: "Date and time when payment was processed" } belongsTo: - { name: Transaction, entity: Transaction } - { name: PaymentMethod, entity: PaymentMethod } PaymentMethod 🏦: properties: - { name: name, type: string, validation: { required: true }, helpText: "Display name for this payment method" } - { name: type, type: choice, options: { values: ["cash", "card", "bank_transfer", "other"] }, validation: { required: true }, helpText: "Type of payment method" } - { name: isActive, type: boolean, default: true, helpText: "Whether this payment method is currently available" } - { name: requiresReference, type: boolean, default: false, helpText: "Whether a reference number is required for this payment type" } - { name: sortOrder, type: number, default: 0, helpText: "Display order in payment method list (lower numbers appear first)" } policies: create: - { access: restricted, allow: Manager } update: - { access: restricted, allow: Manager } Exchange 🔄: properties: - { name: exchangeNumber, type: string, helpText: "Auto-generated unique exchange reference number" } - { name: priceDifference, type: number, helpText: "Price difference between returned and received items" } - { name: additionalPayment, type: money, options: { currency: "MUR" }, default: 0, helpText: "Additional payment required if new items cost more" } - { name: businessProfit, type: number, default: 0, helpText: "Profit/loss for the business from this exchange" } - { name: date, type: timestamp, default: "now", helpText: "Date and time of the exchange" } - { name: reason, type: string, helpText: "Reason for the exchange (size, defect, preference, etc.)" } - { name: status, type: choice, options: { values: ["completed", "pending"] }, default: "completed", helpText: "Current status of the exchange" } belongsTo: - { name: originalTransaction, entity: Transaction } - { name: Customer, entity: Customer } - { name: newTransaction, entity: Transaction } ExchangeItem 🛍️: properties: - { name: type, type: choice, options: { values: ["returned", "received"] }, validation: { required: true }, helpText: "Whether this item was returned by customer or received as replacement" } - { name: unitPrice, type: money, options: { currency: "MUR" }, helpText: "Price of the item at time of exchange" } belongsTo: - { name: Exchange, entity: Exchange } - { name: Product, entity: Product } LostItemExpense 💸: properties: - { name: productValue, type: money, options: { currency: "MUR" }, validation: { required: true }, helpText: "Original value of the lost product" } - { name: resellerCommission, type: money, options: { currency: "MUR" }, validation: { required: true }, helpText: "Commission amount owed to reseller for lost item" } - { name: businessLoss, type: money, options: { currency: "MUR" }, validation: { required: true }, helpText: "Total loss incurred by the business" } - { name: expenseDate, type: date, default: "now", helpText: "Date when the loss was recorded" } - { name: notes, type: text, helpText: "Details about how the item was lost" } belongsTo: - { name: Product, entity: Product } ReconciliationSession 🔍: properties: - { name: startedAt, type: timestamp, default: "now", helpText: "When the reconciliation session started" } - { name: completedAt, type: timestamp, helpText: "When the reconciliation session was completed" } - { name: status, type: choice, options: { values: ["in_progress", "completed"] }, default: "in_progress", helpText: "Current status of the reconciliation" } - { name: totalItemsExpected, type: number, default: 0, helpText: "Total number of items expected to be found" } - { name: totalItemsFound, type: number, default: 0, helpText: "Total number of items actually found during reconciliation" } - { name: totalItemsLost, type: number, default: 0, helpText: "Number of items confirmed as lost" } - { name: totalLossValue, type: money, options: { currency: "MUR" }, default: 0, helpText: "Total monetary value of lost items" } - { name: notes, type: text, helpText: "Notes and observations from the reconciliation process" } belongsTo: - { name: Edition, entity: Edition } Manager 👨‍💻: authenticable: true properties: # Personal Information fields - { name: firstName, type: string, validation: { required: true }, helpText: "First name" } - { name: lastName, type: string, validation: { required: true }, helpText: "Last name" } - { name: dateOfBirth, type: date, helpText: "Date of birth" } # Contact Details fields - { name: email, type: email, validation: { required: true }, helpText: "Email address for login and communications" } - { name: phoneNumber, type: string, helpText: "Contact phone number" } - { name: address, type: text, helpText: "Complete address including street, city, and postal code" } # Other fields - { name: isActive, type: boolean, default: true, helpText: "Whether this account is currently active" } - { name: notes, type: text, helpText: "Internal notes about this manager" } belongsToMany: - { name: BusinessUnit, entity: BusinessUnit } - { name: DiscountRule, entity: DiscountRule } # Admin-only by default (no explicit policies needed) Staff 👨‍💼: authenticable: true properties: - { name: personalInformation, type: group, options: { group: PersonalInformation, multiple: false }, helpText: "Personal information" } - { name: email, type: email, validation: { required: true }, helpText: "Email address for login and communications" } - { name: phoneNumber, type: string, helpText: "Contact phone number" } - { name: isActive, type: boolean, default: true, helpText: "Whether this account is currently active" } belongsToMany: - { name: DiscountRule, entity: DiscountRule } policies: read: - { access: restricted, allow: [Manager, Staff] } update: - { access: restricted, allow: Manager } EmailTemplate 📧: properties: - { name: name, type: string, validation: { required: true }, helpText: "Template name for internal reference" } - { name: subject, type: string, validation: { required: true }, helpText: "Email subject line (supports variables)" } - { name: body, type: text, validation: { required: true }, helpText: "Email body content in HTML format (supports variables)" } - { name: type, type: choice, options: { values: ["participation_created", "drop_off_completed", "contract_checkin", "checkout_report", "participation_paid", "custom"] }, validation: { required: true }, helpText: "Type of email template for system use" } - { name: isActive, type: boolean, default: true, helpText: "Whether this template is currently in use" } - { name: tags, type: text, helpText: "Tags for organizing and searching templates" } belongsTo: - { name: BusinessUnit, entity: BusinessUnit } GeneralSettings ⚙️: single: true slug: general-settings properties: - { name: companyName, type: string, validation: { required: true }, helpText: "Legal name of your company" } - { name: address, type: text, validation: { required: true }, helpText: "Complete company address for invoices and receipts" } - { name: brn, type: string, helpText: "Business Registration Number for official documentation" } - { name: telephone, type: string, helpText: "Main company contact number" } - { name: logo, type: image, helpText: "Company logo for branding on documents" } - { name: website, type: string, helpText: "Company website URL" } policies: read: - { access: public } EmailSettings 📧: single: true slug: email-settings properties: - { name: emailProvider, type: choice, options: { values: ["smtp", "brevo", "disabled"] }, default: "disabled", helpText: "Email service provider to use for sending emails" } - { name: emailFromName, type: string, helpText: "Display name for sender" } - { name: emailFromAddress, type: email, helpText: "Default sender email address" } - { name: emailReplyTo, type: email, helpText: "Email address for customer replies" } - { name: smtpHost, type: string, helpText: "SMTP server hostname (e.g., smtp.gmail.com)" } - { name: smtpPort, type: number, default: 587, helpText: "SMTP server port (usually 587 for TLS)" } - { name: smtpUsername, type: string, helpText: "SMTP authentication username" } - { name: smtpPassword, type: string, helpText: "SMTP authentication password" } - { name: smtpSecure, type: boolean, default: true, helpText: "Use TLS/SSL for secure email sending" } - { name: brevoApiKey, type: string, helpText: "API key for Brevo email service" } - { name: testEmailAddress, type: email, helpText: "Email address for testing email configuration" } policies: read: - { access: restricted, allow: Manager } DiscountRule 🏷️: properties: - { name: name, type: string, validation: { required: true }, helpText: "Name of the discount rule" } - { name: description, type: text, helpText: "Detailed description of when and how this discount applies" } - { name: category, type: choice, options: { values: ["custom", "staff", "customer"] }, validation: { required: true }, helpText: "Category of users this discount applies to" } - { name: type, type: choice, options: { values: ["percentage", "flat"] }, validation: { required: true }, helpText: "Whether discount is a percentage or flat amount" } - { name: value, type: number, validation: { required: true, min: 0 }, helpText: "Discount value (percentage or amount based on type)" } - { name: minimumPurchase, type: money, options: { currency: "MUR" }, default: 0, helpText: "Minimum purchase amount required for discount" } - { name: maximumDiscount, type: money, options: { currency: "MUR" }, default: 0, helpText: "Maximum discount amount (0 for unlimited)" } - { name: quantityLimit, type: number, default: 0, helpText: "Maximum quantity per transaction (0 for unlimited)" } - { name: startDate, type: date, helpText: "Date when discount becomes active" } - { name: endDate, type: date, helpText: "Date when discount expires" } - { name: usageLimit, type: number, default: 0, helpText: "Maximum times this discount can be used (0 for unlimited)" } - { name: usageCount, type: number, default: 0, helpText: "Number of times this discount has been used" } - { name: isActive, type: boolean, default: true, helpText: "Whether this discount is currently active" } - { name: isStackable, type: boolean, default: false, helpText: "Can be combined with other discounts" } belongsToMany: - { name: Customer, entity: Customer } - { name: Staff, entity: Staff } - { name: Manager, entity: Manager } PromoCode 🎟️: properties: - { name: code, type: string, validation: { required: true }, helpText: "Unique promo code that customers will enter" } - { name: description, type: text, helpText: "Description of the promotion for internal reference" } - { name: isActive, type: boolean, default: true, helpText: "Whether this promo code is currently active" } - { name: expiryDate, type: date, helpText: "Date when promo code expires" } - { name: usageLimit, type: number, default: 0, helpText: "Maximum times this code can be used (0 for unlimited)" } - { name: usageCount, type: number, default: 0, helpText: "Number of times this code has been used" } - { name: minimumPurchase, type: money, options: { currency: "MUR" }, default: 0, helpText: "Minimum purchase required to use this code" } belongsTo: - { name: DiscountRule, entity: DiscountRule } TransactionDiscount 💸: properties: - { name: discountAmount, type: money, options: { currency: "MUR" }, validation: { required: true }, helpText: "Total discount amount applied to the transaction" } - { name: appliedAt, type: timestamp, default: "now", helpText: "When the discount was applied" } - { name: reason, type: text, helpText: "Reason for applying the discount" } - { name: promoCode, type: string, helpText: "Promo code used (if applicable)" } belongsTo: - { name: Transaction, entity: Transaction } - { name: DiscountRule, entity: DiscountRule } StaffDiscountUsage 📊: properties: - { name: month, type: number, validation: { required: true, min: 1, max: 12 }, helpText: "Month of usage (1-12)" } - { name: year, type: number, validation: { required: true, min: 2020 }, helpText: "Year of usage" } - { name: quantityUsed, type: number, default: 0, validation: { min: 0 }, helpText: "Number of items purchased with staff discount" } - { name: lastUpdated, type: timestamp, default: "now", helpText: "Last time this record was updated" } belongsTo: - { name: staffMember, entity: Staff } - { name: discountRule, entity: DiscountRule } # Groups section - Reusable property groups groups: PersonalInformation: properties: - { name: firstName, type: string, validation: { required: true }, helpText: "First name" } - { name: lastName, type: string, validation: { required: true }, helpText: "Last name" } - { name: dateOfBirth, type: date, helpText: "Date of birth" } SignatureDetails: properties: - { name: staffName, type: string, helpText: "Name of staff member" } - { name: staffSignature, type: file, helpText: "Digital signature of staff member" } - { name: resellerSignature, type: file, helpText: "Digital signature of reseller" } - { name: signatureDate, type: timestamp, default: "now", helpText: "Date and time of signature" } PaymentDetails: properties: - { name: payoutStatus, type: boolean, default: false, helpText: "Whether payment has been processed" } - { name: payoutDate, type: date, helpText: "Date when payment was processed" } - { name: payoutAmount, type: money, options: { currency: "MUR" }, helpText: "Total amount paid" } - { name: paymentReference, type: string, helpText: "Payment reference number" }