Skip to main content
Technical ManualLooking for step-by-step instructions? In the User Manual: Sales Categories

Items

The Items module keeps the sales categories of a business: the legacy "departments" the daily sales and purchases are entered by. They are an Orchard Core content type. Products and services for invoices come with the service sales module.

ProjectsCrestApps.Almohaseb.Items (module), CrestApps.Almohaseb.Items.Core (part, index, rules, directory)
FeatureCrestApps.Almohaseb.Items (Sales categories), enabled by both business recipes
LegacyBOOK-21 Departments, BOOK-22 Departments Merge

Features and dependencies​

CrestApps.Almohaseb.Items depends on CrestApps.Almohaseb, CrestApps.Almohaseb.Ledger (each category has a ledger account) and OrchardCore.Contents. It is a managed feature, for the same reason as Contacts: business recipes cannot enable dependency-only features.

The SalesCategory type​

ItemsMigrations.CreateAsync (version 1) defines AlmohasebSalesCategory: creatable, listable, securable, not draftable or versionable, with AlmohasebSalesCategoryPart. Version 2 seeds the Sales Tax category. Version 3 defines AlmohasebSalesTaxExemption (see Exemption reasons).

PropertyTypeNotes
Namestring100; unique; also the display text.
KindSalesCategoryKindRetail (0), Service (1), Purchase (2): the legacy RETAIL, SERVICE and PURCHASE department types.
RevenuePercentdecimal0 to 100, default 100: the share of the sales that is income (lottery commission).
AlwaysMinusboolAlways subtracted; never taxable.
IsTaxable, TaxRatebool, decimalThe rate is a percentage with at most four decimals (6 = 6%). The legacy app stored a fraction in the same field (Clover wrote 0.0825).
ExemptionIdstringThe legacy "item type": the content item id of a AlmohasebSalesTaxExemption.
IncludedInSalesTax, IncludedInFinancialStatementsboolThe legacy "Included in 508*" and "Included in F.S". Both default to true.
UsedOnPurchasesboolRetail only: also on the purchases screen (the legacy "connected department").
AccountIdstringThe ledger account.
ParentIdstringThe category it is merged into (replaces tbl_parent_departments and ItemChildTo).
SortOrderintThe position within the regular categories.
SystemKeystringSalesTax, Deposit or BottleRefund for system categories (ItemsConstants.SystemKeys).
IsActivebool

SalesCategoryPartIndex (latest version) holds ContentItemId, Name, Kind, SystemKey, ParentId, UsedOnPurchases and IsActive, with database indexes on IsActive, Kind, SystemKey, ParentId and ContentItemId. The table definition is the public static method ItemsMigrations.CreateIndexTableAsync.

Exemption reasons​

The reasons sales are exempt differ by state, so they are data, not an enum: the AlmohasebSalesTaxExemption content type (creatable, listable, securable) with AlmohasebSalesTaxExemptionPart (Name, ReturnLine, IsActive). The business recipes seed the Michigan 5080 deduction lines 5a to 5k with fixed content item ids (exm-resale, exm-food, ...); the jurisdiction packs will seed other states. A business can add its own reasons and fields. Books > Sales tax exemptions (id almohasebSalesTaxExemptions) is the content list.

System categories​

SystemSalesCategories.All defines the three system categories and their locked fields. The migration creates only Sales Tax, which every business has and the daily sales find by key; it is safe to run again. Deposit and Bottle Refund are c-store categories, so the c-store recipe creates them with their SystemKey.

KeyNameTreatmentAccount (ledger system key)
SalesTaxSales TaxNot taxable, not on the returnSalesTaxPayable
DepositDepositNot taxable, on the returnSales
BottleRefundBottle RefundAlways minus, not on the returnSales

SystemSalesCategories.ApplyLockedFields is applied on every save of a system category, so a forged post cannot change its kind, sales tax treatment, parent or active flag; the name, position, revenue share, statement flag and account stay editable. Unlike the legacy app, which found them by title, system categories are found by key and can be renamed. SalesCategoryPartHandler.RemovingAsync cancels the deletion of a system category and of a category others are merged into, with a notice.

Rules​

SalesCategoryRules (the legacy ItemValidator and the department grid's linked cells):

  • Normalize: an always-minus category is not taxable and has no rate or exemption; a taxable category has no exemption; a non-taxable one has no rate; only retail categories are used on purchases.
  • Validate (SalesCategoryValidationCode): name required and at most 100; revenue share 0 to 100; a taxable category has a rate above 0 and at most 100, with at most four decimals; a non-taxable category on the return that is not always minus, not a system category and not a purchase category has an exemption.
  • ValidateParent: the parent exists, has the same kind, is not merged itself and is not the category; a category others are merged into cannot be merged.
  • Order: the legacy entry-screen order — regular categories by SortOrder and name, then Deposit, Bottle Refund, the always-minus categories, and Sales Tax last.

The duplicate name check runs in SalesCategoryPartDisplayDriver against the index.

Services​

ServiceLifetimeWhat it does
ISalesCategoryDirectory / SalesCategoryDirectoryScopedListForSalesAsync() (active retail and service categories), ListForPurchasesAsync() (active purchase categories and retail ones used on purchases), both in SalesCategoryRules.Order; FindBySystemKeyAsync(key).

Admin​

Books > Sales categories (id almohasebSalesCategories) is the content list of the type, shown with ListContent_SalesCategory. The driver adds a SalesCategorySummary shape to SummaryAdmin.

Permissions​

The module declares no permission of its own; the securable type has the Orchard Core content permissions (List Sales category content items, Edit Sales category for others and so on).

Recipes and deployment​

Both business recipes enable CrestApps.Almohaseb.Items and use a Content step to seed the starter data: the Michigan exemption reasons, starter sales categories (c-store: Grocery, Food, Beer and wine, Liquor, Tobacco, Lottery, Deposit, Bottle Refund, Lottery payouts, Inventory; service: Labor, Parts and materials, Materials) and starter vendor categories. The seeded items have fixed ids (cat-grocery, exm-food, vnc-suppliers, ...) so other recipe items can refer to them, and no account: a category without one posts to the system Sales account. A recipe that creates a system category must set SystemKey. Categories and reasons are content items, so the content deployment steps export and import them too.

Extension points​

  • Pickers on entry screens: use ISalesCategoryDirectory.
  • More data on a category or a reason: attach a part or fields to the AlmohasebSalesCategory or AlmohasebSalesTaxExemption type, in a migration or in the content type editor.

See also​