@tailor-platform/erp-kit 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +77 -50
- package/dist/cli.js +693 -553
- package/package.json +4 -2
- package/schemas/module/command.yml +1 -0
- package/schemas/module/model.yml +9 -0
- package/schemas/module/query.yml +53 -0
- package/skills/1-module-docs/SKILL.md +4 -0
- package/{rules/module-development → skills/1-module-docs/references}/structure.md +2 -7
- package/skills/2-module-feature-breakdown/SKILL.md +6 -0
- package/{rules/module-development → skills/2-module-feature-breakdown/references}/commands.md +0 -6
- package/{rules/module-development → skills/2-module-feature-breakdown/references}/models.md +0 -5
- package/skills/2-module-feature-breakdown/references/structure.md +22 -0
- package/skills/3-module-doc-review/SKILL.md +6 -0
- package/skills/3-module-doc-review/references/commands.md +54 -0
- package/skills/3-module-doc-review/references/models.md +29 -0
- package/{rules/module-development → skills/3-module-doc-review/references}/testing.md +0 -6
- package/skills/4-module-tdd-implementation/SKILL.md +24 -6
- package/skills/4-module-tdd-implementation/references/commands.md +45 -0
- package/{rules/sdk-best-practices → skills/4-module-tdd-implementation/references}/db-relations.md +0 -5
- package/{rules/module-development → skills/4-module-tdd-implementation/references}/errors.md +0 -5
- package/{rules/module-development → skills/4-module-tdd-implementation/references}/exports.md +0 -5
- package/skills/4-module-tdd-implementation/references/models.md +30 -0
- package/skills/4-module-tdd-implementation/references/structure.md +22 -0
- package/skills/4-module-tdd-implementation/references/testing.md +37 -0
- package/skills/5-module-implementation-review/SKILL.md +8 -0
- package/skills/5-module-implementation-review/references/commands.md +45 -0
- package/skills/5-module-implementation-review/references/errors.md +7 -0
- package/skills/5-module-implementation-review/references/exports.md +8 -0
- package/skills/5-module-implementation-review/references/models.md +30 -0
- package/skills/5-module-implementation-review/references/testing.md +29 -0
- package/skills/app-compose-1-requirement-analysis/SKILL.md +4 -0
- package/{rules/app-compose → skills/app-compose-1-requirement-analysis/references}/structure.md +0 -5
- package/skills/app-compose-2-requirements-breakdown/SKILL.md +7 -0
- package/{rules/app-compose/frontend → skills/app-compose-2-requirements-breakdown/references}/screen-detailview.md +0 -6
- package/{rules/app-compose/frontend → skills/app-compose-2-requirements-breakdown/references}/screen-form.md +0 -6
- package/{rules/app-compose/frontend → skills/app-compose-2-requirements-breakdown/references}/screen-listview.md +0 -6
- package/skills/app-compose-2-requirements-breakdown/references/structure.md +27 -0
- package/skills/app-compose-3-doc-review/SKILL.md +4 -0
- package/skills/app-compose-3-doc-review/references/structure.md +27 -0
- package/skills/app-compose-4-design-mock/SKILL.md +8 -0
- package/{rules/app-compose/frontend → skills/app-compose-4-design-mock/references}/component.md +0 -5
- package/skills/app-compose-4-design-mock/references/screen-detailview.md +106 -0
- package/skills/app-compose-4-design-mock/references/screen-form.md +139 -0
- package/skills/app-compose-4-design-mock/references/screen-listview.md +153 -0
- package/skills/app-compose-4-design-mock/references/structure.md +27 -0
- package/skills/app-compose-5-design-mock-review/SKILL.md +7 -0
- package/skills/app-compose-5-design-mock-review/references/component.md +50 -0
- package/skills/app-compose-5-design-mock-review/references/screen-detailview.md +106 -0
- package/skills/app-compose-5-design-mock-review/references/screen-form.md +139 -0
- package/skills/app-compose-5-design-mock-review/references/screen-listview.md +153 -0
- package/skills/app-compose-6-implementation-spec/SKILL.md +5 -0
- package/{rules/app-compose/backend → skills/app-compose-6-implementation-spec/references}/auth.md +0 -6
- package/skills/app-compose-6-implementation-spec/references/structure.md +27 -0
- package/src/cli.ts +8 -90
- package/src/commands/app/index.ts +74 -0
- package/src/commands/check.test.ts +2 -1
- package/src/commands/check.ts +1 -0
- package/src/commands/init.test.ts +30 -19
- package/src/commands/init.ts +76 -43
- package/src/commands/module/index.ts +85 -0
- package/src/commands/module/list.test.ts +62 -0
- package/src/commands/module/list.ts +64 -0
- package/src/commands/scaffold.test.ts +5 -0
- package/src/commands/scaffold.ts +2 -1
- package/src/commands/sync-check.test.ts +28 -0
- package/src/commands/sync-check.ts +6 -0
- package/src/integration.test.ts +6 -8
- package/src/module.ts +4 -3
- package/src/modules/primitives/docs/models/Currency.md +4 -0
- package/src/modules/primitives/docs/models/ExchangeRate.md +4 -1
- package/src/modules/primitives/docs/models/Unit.md +4 -1
- package/src/modules/primitives/docs/models/UoMCategory.md +2 -0
- package/src/modules/primitives/index.ts +2 -2
- package/src/modules/primitives/module.ts +5 -3
- package/src/modules/primitives/permissions.ts +0 -2
- package/src/modules/primitives/{command → query}/convertAmount.test.ts +2 -19
- package/src/modules/primitives/query/convertAmount.ts +122 -0
- package/src/modules/primitives/{command → query}/convertQuantity.test.ts +2 -13
- package/src/modules/primitives/{command → query}/convertQuantity.ts +4 -6
- package/src/modules/shared/defineQuery.test.ts +28 -0
- package/src/modules/shared/defineQuery.ts +16 -0
- package/src/modules/shared/internal.ts +2 -1
- package/src/modules/shared/types.ts +8 -0
- package/src/modules/user-management/docs/models/AuditEvent.md +2 -0
- package/src/modules/user-management/docs/models/Permission.md +2 -0
- package/src/modules/user-management/docs/models/Role.md +2 -0
- package/src/modules/user-management/docs/models/RolePermission.md +2 -0
- package/src/modules/user-management/docs/models/User.md +2 -0
- package/src/modules/user-management/docs/models/UserRole.md +2 -0
- package/src/schemas.ts +1 -0
- package/rules/app-compose/frontend/auth.md +0 -55
- package/rules/app-compose/frontend/page.md +0 -86
- package/rules/module-development/cross-module-type-injection.md +0 -28
- package/rules/module-development/dependency-modules.md +0 -24
- package/rules/module-development/executors.md +0 -67
- package/rules/module-development/sync-vs-async-operations.md +0 -83
- package/rules/sdk-best-practices/sdk-docs.md +0 -14
- package/src/modules/primitives/command/convertAmount.ts +0 -126
- /package/src/modules/primitives/docs/{commands → queries}/ConvertAmount.md +0 -0
- /package/src/modules/primitives/docs/{commands → queries}/ConvertQuantity.md +0 -0
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
paths:
|
|
3
|
-
- "modules/*/src/"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Sync vs Async Operations
|
|
7
|
-
|
|
8
|
-
## Decision Criteria
|
|
9
|
-
|
|
10
|
-
Choose synchronous or asynchronous execution based on:
|
|
11
|
-
|
|
12
|
-
1. **Number of affected records** - How many records need processing?
|
|
13
|
-
2. **Data growth trajectory** - Will the operation slow down as data grows?
|
|
14
|
-
|
|
15
|
-
| Scenario | Approach | Implementation |
|
|
16
|
-
| ----------------------------------- | ------------------ | --------------------------- |
|
|
17
|
-
| Single record, bounded complexity | **Synchronous** | Inline in command |
|
|
18
|
-
| Single record, unbounded complexity | **Consider async** | Evaluate growth |
|
|
19
|
-
| Multiple records | **Asynchronous** | Executor with `jobFunction` |
|
|
20
|
-
|
|
21
|
-
## Growth Considerations
|
|
22
|
-
|
|
23
|
-
Ask: "How does this operation scale as the system grows?"
|
|
24
|
-
|
|
25
|
-
- **Bounded**: User status update (always 1 record, O(1))
|
|
26
|
-
- **Bounded**: Single user permission recompute (limited by reasonable role/permission counts)
|
|
27
|
-
- **Unbounded**: All users with role X (grows with user base, O(n))
|
|
28
|
-
- **Unbounded**: All orders in date range (grows with transaction volume)
|
|
29
|
-
|
|
30
|
-
When in doubt, monitor operation timing in production and migrate to async if latency increases.
|
|
31
|
-
|
|
32
|
-
## Rationale
|
|
33
|
-
|
|
34
|
-
- **Synchronous**: Acceptable when operation time is predictable and bounded
|
|
35
|
-
- **Asynchronous**: Required when operation time scales with data volume
|
|
36
|
-
|
|
37
|
-
## Pattern: Synchronous (Single Record)
|
|
38
|
-
|
|
39
|
-
Commands that affect a single known record should execute the operation inline and return the result:
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
// In command - recompute immediately
|
|
43
|
-
const updatedUser = await recomputeUserPermissions(db, input.userId);
|
|
44
|
-
return { userRole, user: updatedUser, auditEvent };
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Pattern: Asynchronous (Multiple Records)
|
|
48
|
-
|
|
49
|
-
Commands that affect an unknown number of records should delegate to an executor:
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
// Command just creates/deletes the record
|
|
53
|
-
// Executor handles recomputation asynchronously
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Create executors with record triggers:
|
|
57
|
-
|
|
58
|
-
- `recordCreatedTrigger` - React to inserts
|
|
59
|
-
- `recordDeletedTrigger` - React to deletes
|
|
60
|
-
- Use `kind: "jobFunction"` for extended execution
|
|
61
|
-
|
|
62
|
-
## Example: Permission Recomputation
|
|
63
|
-
|
|
64
|
-
| Command | Affected | Approach |
|
|
65
|
-
| ------------------------ | ----------------------- | --------------- |
|
|
66
|
-
| assignRoleToUser | 1 user | Sync in command |
|
|
67
|
-
| revokeRoleFromUser | 1 user | Sync in command |
|
|
68
|
-
| assignPermissionToRole | N users (all with role) | Async executor |
|
|
69
|
-
| revokePermissionFromRole | N users (all with role) | Async executor |
|
|
70
|
-
|
|
71
|
-
## Trade-offs
|
|
72
|
-
|
|
73
|
-
**Synchronous:**
|
|
74
|
-
|
|
75
|
-
- Immediate consistency
|
|
76
|
-
- Simpler error handling
|
|
77
|
-
- Blocks request until complete
|
|
78
|
-
|
|
79
|
-
**Asynchronous:**
|
|
80
|
-
|
|
81
|
-
- Eventual consistency
|
|
82
|
-
- Non-blocking requests
|
|
83
|
-
- Requires executor infrastructure
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
paths:
|
|
3
|
-
- "modules/*/src/db/*.ts"
|
|
4
|
-
- "modules/*/src/executor/*.ts"
|
|
5
|
-
- "modules/*/src/workflow/*.ts"
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# SDK Docs Reference
|
|
9
|
-
|
|
10
|
-
Read the official Tailor SDK documentation for database models, executors:
|
|
11
|
-
|
|
12
|
-
- [TailorDB Models](https://raw.githubusercontent.com/tailor-platform/sdk/refs/heads/main/packages/sdk/docs/services/tailordb.md)
|
|
13
|
-
- [Executors](https://raw.githubusercontent.com/tailor-platform/sdk/refs/heads/main/packages/sdk/docs/services/executors.md)
|
|
14
|
-
- [Workflow](https://raw.githubusercontent.com/tailor-platform/sdk/refs/heads/main/packages/sdk/docs/services/workflow.md)
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { defineCommand } from "../../shared/internal";
|
|
2
|
-
import { DB } from "../generated/kysely-tailordb";
|
|
3
|
-
import {
|
|
4
|
-
CurrencyNotFoundError,
|
|
5
|
-
ExchangeRateNotFoundError,
|
|
6
|
-
InactiveCurrencyError,
|
|
7
|
-
} from "../lib/errors";
|
|
8
|
-
import { permissions } from "../permissions";
|
|
9
|
-
|
|
10
|
-
export interface ConvertAmountInput {
|
|
11
|
-
amount: number;
|
|
12
|
-
sourceCurrencyCode: string;
|
|
13
|
-
targetCurrencyCode: string;
|
|
14
|
-
conversionDate: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Function: convertAmount
|
|
19
|
-
*
|
|
20
|
-
* Converts a monetary amount from one currency to another using the applicable
|
|
21
|
-
* exchange rate for a given date. Currencies are identified by their ISO 4217
|
|
22
|
-
* code (e.g., "USD", "EUR", "JPY"). The function looks up the most recent exchange
|
|
23
|
-
* rate on or before the specified date. If no direct rate exists, it calculates
|
|
24
|
-
* the inverse rate. Result is rounded to the target currency's decimal precision.
|
|
25
|
-
*/
|
|
26
|
-
export const convertAmount = defineCommand(
|
|
27
|
-
permissions.convertAmount,
|
|
28
|
-
async (db: DB, input: ConvertAmountInput) => {
|
|
29
|
-
// 1. Validate source currency exists
|
|
30
|
-
const sourceCurrency = await db
|
|
31
|
-
.selectFrom("Currency")
|
|
32
|
-
.selectAll()
|
|
33
|
-
.where("code", "=", input.sourceCurrencyCode)
|
|
34
|
-
.executeTakeFirst();
|
|
35
|
-
|
|
36
|
-
if (!sourceCurrency) {
|
|
37
|
-
throw new CurrencyNotFoundError(input.sourceCurrencyCode);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// 2. Validate target currency exists
|
|
41
|
-
const targetCurrency = await db
|
|
42
|
-
.selectFrom("Currency")
|
|
43
|
-
.selectAll()
|
|
44
|
-
.where("code", "=", input.targetCurrencyCode)
|
|
45
|
-
.executeTakeFirst();
|
|
46
|
-
|
|
47
|
-
if (!targetCurrency) {
|
|
48
|
-
throw new CurrencyNotFoundError(input.targetCurrencyCode);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 3. Validate both currencies are active
|
|
52
|
-
if (!sourceCurrency.isActive) {
|
|
53
|
-
throw new InactiveCurrencyError(input.sourceCurrencyCode);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (!targetCurrency.isActive) {
|
|
57
|
-
throw new InactiveCurrencyError(input.targetCurrencyCode);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// 4. Same currency - return original amount
|
|
61
|
-
if (sourceCurrency.id === targetCurrency.id) {
|
|
62
|
-
return {
|
|
63
|
-
convertedAmount: input.amount,
|
|
64
|
-
exchangeRate: 1,
|
|
65
|
-
sourceCurrency,
|
|
66
|
-
targetCurrency,
|
|
67
|
-
exchangeRateRecord: null,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// 5. Find direct exchange rate (most recent on or before conversion date)
|
|
72
|
-
const conversionDate = new Date(input.conversionDate);
|
|
73
|
-
const directRate = await db
|
|
74
|
-
.selectFrom("ExchangeRate")
|
|
75
|
-
.selectAll()
|
|
76
|
-
.where("sourceCurrencyId", "=", sourceCurrency.id)
|
|
77
|
-
.where("targetCurrencyId", "=", targetCurrency.id)
|
|
78
|
-
.where("effectiveDate", "<=", conversionDate)
|
|
79
|
-
.orderBy("effectiveDate", "desc")
|
|
80
|
-
.executeTakeFirst();
|
|
81
|
-
|
|
82
|
-
let exchangeRate: number;
|
|
83
|
-
let exchangeRateRecord = null;
|
|
84
|
-
|
|
85
|
-
if (directRate) {
|
|
86
|
-
exchangeRate = directRate.rate;
|
|
87
|
-
exchangeRateRecord = directRate;
|
|
88
|
-
} else {
|
|
89
|
-
// 6. Try inverse rate
|
|
90
|
-
const inverseRate = await db
|
|
91
|
-
.selectFrom("ExchangeRate")
|
|
92
|
-
.selectAll()
|
|
93
|
-
.where("sourceCurrencyId", "=", targetCurrency.id)
|
|
94
|
-
.where("targetCurrencyId", "=", sourceCurrency.id)
|
|
95
|
-
.where("effectiveDate", "<=", conversionDate)
|
|
96
|
-
.orderBy("effectiveDate", "desc")
|
|
97
|
-
.executeTakeFirst();
|
|
98
|
-
|
|
99
|
-
if (!inverseRate) {
|
|
100
|
-
throw new ExchangeRateNotFoundError(
|
|
101
|
-
input.sourceCurrencyCode,
|
|
102
|
-
input.targetCurrencyCode,
|
|
103
|
-
input.conversionDate,
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
exchangeRate = 1 / inverseRate.rate;
|
|
108
|
-
exchangeRateRecord = inverseRate;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 7. Calculate converted amount
|
|
112
|
-
const rawResult = input.amount * exchangeRate;
|
|
113
|
-
|
|
114
|
-
// 8. Round to target currency's decimal places
|
|
115
|
-
const roundingFactor = Math.pow(10, targetCurrency.decimalPlaces);
|
|
116
|
-
const convertedAmount = Math.round(rawResult * roundingFactor) / roundingFactor;
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
convertedAmount,
|
|
120
|
-
exchangeRate,
|
|
121
|
-
sourceCurrency,
|
|
122
|
-
targetCurrency,
|
|
123
|
-
exchangeRateRecord,
|
|
124
|
-
};
|
|
125
|
-
},
|
|
126
|
-
);
|
|
File without changes
|
|
File without changes
|