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

Assets

The Assets module keeps the fixed asset register, posts straight-line depreciation every month and records asset sales with their gain or loss. It replaces the legacy assets entry, depreciation schedule and asset sale (BOOK-24 to BOOK-26).

ProjectsCrestApps.Almohaseb.Assets (module), CrestApps.Almohaseb.Assets.Core (part, schedule, posting sources, rules, sync service)
FeatureCrestApps.Almohaseb.Assets (Fixed assets), enabled by both business recipes; depends on Accounting, Jobs and Customers & vendors

FixedAsset​

AssetsMigrations.CreateAsync (version 1) defines the AlmohasebFixedAsset content type (creatable, listable, securable) with AlmohasebFixedAssetPart, so a business can add its own fields. AlmohasebFixedAssetPart holds Description, VendorId, PurchaseDate, Cost, SalvageValue, DepreciationYears (0 = not depreciated), PaymentMethodId, AssetAccountId (default: the system FixedAssets account, 1700, added to the ledger by its migration version 8), InvoiceNumber, SoldDate, SoldAmount and SoldPaymentMethodId; IsSold is computed. FixedAssetPartIndex (ContentItemId, PurchaseDate, DepreciationYears, IsSold) serves the depreciation job.

Depreciation schedule​

DepreciationSchedule.Build(purchase, cost, salvage, years, sold) is the legacy formula as a pure rule: annual = floor2((cost − salvage) / years), monthly = floor2(annual / 12); the first entry on the purchase date is floor2((30 − day + 1) / 30 × monthly); 12 × years − 1 monthly entries on month ends; a final remainder so the total is cost − salvage. A sale keeps the months before it, re-dates the sale month on the sale date with floor2(min(day + 1, 30) / 30 × monthly) (never more than that month had), and drops the rest. GetBookValue is cost less the entries through a date.

Posting​

Three posting sources, each with its rule:

Source (source type)Source idEntry
AssetPurchase (Assets.Purchase)the asset idDr the asset account; Cr the payment method's account (Accounts Payable on account)
AssetDepreciation (Assets.Depreciation){asset id}:{yyyyMM}Dr Depreciation Expenses; Cr Accumulated Depreciation
AssetSale (Assets.Sale)the asset idDr the money received, Dr Accumulated Depreciation (all taken through the sale), Cr the asset account (cost); the difference Cr Profit From Asset Sale or Dr Loss From Asset Sale, found by system key (not by title, as the legacy app did)

IFixedAssetPostingService.SyncAsync(asset, part, today) posts the purchase, every month due through today and the sale; it unposts the months posted before that are no longer due (a shorter schedule, a sale), found with IJournalEntryStore.ListCurrentSourceIdsAsync. Each revision is the values it posts (date, amount, cost, accounts), so a save that changes nothing posted leaves every entry as it is — fixing the legacy save that regenerated the schedule of every asset, sold ones included (BOOK-25). RemoveAsync unposts everything.

FixedAssetPartDisplayDriver.UpdateAsync validates the asset (description, purchase date, cost with cents, salvage between 0 and the cost, 0 to 50 years, a sale on or after the purchase) and runs SyncAsync in the same unit of work; a posting error (a closed period) becomes a model error, so the save is refused. FixedAssetPartHandler.RemovingAsync unposts a deleted asset and cancels the deletion when that fails.

Monthly depreciation​

DepreciationBackgroundTask runs every day at 04:30 (30 4 * * *) and, when an unsold depreciable asset exists, enqueues the CrestApps.Almohaseb.Assets.Depreciation job (idempotency key due). DepreciationJobHandler syncs 50 assets per batch, with the last index row as its checkpoint; assets whose posting fails count as warnings.

Admin​

Books > Fixed assets (id almohasebFixedAssets) is the content list of the type, shown with ListContent_FixedAsset. The summary shows the purchase, the cost and the book value or Sold; the editor shows the schedule.

Permissions​

The securable type has the Orchard Core content permissions; the module declares none of its own.

See also​