-
Notifications
You must be signed in to change notification settings - Fork 135
Expand file tree
/
Copy pathlogic.ts
More file actions
91 lines (79 loc) · 3.5 KB
/
Copy pathlogic.ts
File metadata and controls
91 lines (79 loc) · 3.5 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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import type { ITemplateModel, ILateDeliveryAndPenaltyRequest, ILateDeliveryAndPenaltyResponse, IPaymentObligationEvent } from './generated/org.accordproject.latedeliveryandpenalty@0.2.0';
import type { IDuration } from './generated/org.accordproject.time@0.3.0';
import type { IMonetaryAmount, CurrencyCode } from './generated/org.accordproject.money@0.3.0';
type LateDeliveryAndPenaltyResult = {
result: ILateDeliveryAndPenaltyResponse;
events: object[];
};
type DurationUnit = IDuration['unit'];
const TemporalUnit = {
seconds: 'seconds' as DurationUnit,
minutes: 'minutes' as DurationUnit,
hours: 'hours' as DurationUnit,
days: 'days' as DurationUnit,
weeks: 'weeks' as DurationUnit,
};
function durationToDays(duration: IDuration): number {
switch (duration.unit) {
case TemporalUnit.days: return duration.amount;
case TemporalUnit.weeks: return duration.amount * 7;
case TemporalUnit.hours: return duration.amount / 24;
default: throw new Error(`Cannot convert ${duration.unit} to days`);
}
}
function monetary(doubleValue: number, currencyCode: CurrencyCode): IMonetaryAmount {
return {
$class: 'org.accordproject.money@0.3.0.MonetaryAmount',
doubleValue,
currencyCode,
};
}
// @ts-ignore TemplateLogic is injected by the runtime
class LateDeliveryAndPenaltyLogic extends TemplateLogic<ITemplateModel> {
async trigger(data: ITemplateModel, request: ILateDeliveryAndPenaltyRequest): Promise<LateDeliveryAndPenaltyResult> {
const now = new Date();
const agreed = new Date(request.agreedDelivery);
const currencyCode = request.goodsValue.currencyCode;
if (agreed >= now) {
throw new Error('Cannot exercise late delivery before delivery date');
}
if (data.forceMajeure && request.forceMajeure) {
return {
result: {
$class: 'org.accordproject.latedeliveryandpenalty@0.2.0.LateDeliveryAndPenaltyResponse',
$timestamp: now,
penalty: monetary(0, currencyCode),
buyerMayTerminate: true,
},
events: [],
};
}
const diffMs = now.getTime() - agreed.getTime();
const diffDays = diffMs / (1000 * 60 * 60 * 24);
const penaltyDurationDays = durationToDays(data.penaltyDuration);
const diffRatio = diffDays / penaltyDurationDays;
const goodsValue = request.goodsValue.doubleValue;
const penalty = diffRatio * (data.penaltyPercentage / 100.0) * goodsValue;
const cap = (data.capPercentage / 100.0) * goodsValue;
const capped = Math.min(penalty, cap);
const cappedAmount = monetary(capped, currencyCode);
const terminationDays = durationToDays(data.termination);
const buyerMayTerminate = diffDays > terminationDays;
const event: IPaymentObligationEvent = {
$class: 'org.accordproject.latedeliveryandpenalty@0.2.0.PaymentObligationEvent',
$timestamp: now,
amount: cappedAmount,
description: `${data.seller} should pay penalty amount to ${data.buyer}`,
};
return {
result: {
$class: 'org.accordproject.latedeliveryandpenalty@0.2.0.LateDeliveryAndPenaltyResponse',
$timestamp: now,
penalty: cappedAmount,
buyerMayTerminate,
},
events: [event],
};
}
}
export default LateDeliveryAndPenaltyLogic;