-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathopportunity-line-item.object.ts
More file actions
64 lines (61 loc) · 2.27 KB
/
Copy pathopportunity-line-item.object.ts
File metadata and controls
64 lines (61 loc) · 2.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
import { ObjectSchema, Field } from '@objectstack/spec/data';
import { cel } from '@objectstack/spec';
/**
* Opportunity Line Item — the product/quantity detail rows of an Opportunity.
*
* This is the canonical master-detail "header + line items" shape (mirrors the
* showcase Invoice ↔ InvoiceLine pair): an opportunity is quoted together with
* the products on it, entered in ONE atomic transaction. So the back-pointer
* `opportunity` field declares `inlineEdit: 'grid'` — every standard New/Edit
* Opportunity form (and the lead-conversion wizard's Opportunity step) renders
* an editable line-item grid, and the parent `Opportunity.line_total` rolls the
* line amounts up server-side.
*/
export const OpportunityLineItem = ObjectSchema.create({
name: 'crm_opportunity_line_item',
label: 'Line Item',
pluralLabel: 'Line Items',
icon: 'list',
description: 'A single product line on an opportunity quote.',
fields: {
opportunity: Field.masterDetail('crm_opportunity', {
label: 'Opportunity',
required: true,
deleteBehavior: 'cascade',
// Thin, high-volume product rows → the editable grid form factor.
inlineEdit: 'grid',
inlineTitle: 'Products',
inlineAmountField: 'amount',
}),
product: Field.text({
label: 'Product',
required: true,
maxLength: 200,
}),
quantity: Field.number({
label: 'Qty',
required: true,
min: 1,
defaultValue: 1,
}),
unit_price: Field.currency({
label: 'Unit Price',
scale: 2,
min: 0,
}),
// Amount = Qty × Unit Price. Kept as a *stored* currency column (so the
// parent Opportunity.line_total summary can roll it up — summary aggregation
// reads stored columns, not on-read formula fields), but the `expression`
// makes the line-item grid render it READ-ONLY and recompute it live
// client-side as quantity/unit_price change, then persist the computed
// value. The server stores the client-sent value as-is. (Mirrors the
// showcase InvoiceLine.amount pattern.)
amount: Field.currency({
label: 'Amount',
scale: 2,
min: 0,
expression: cel`record.quantity * record.unit_price`,
}),
},
});