Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
import { assert } from '../assertion/assertion.js';
|
||||
|
||||
/**
|
||||
* ISO-4217 currency codes.
|
||||
*/
|
||||
const CURRENCY_CODES = ['USD', 'EUR', 'CAD', 'GBP'] as const;
|
||||
|
||||
export type CurrencyCode = (typeof CURRENCY_CODES)[number];
|
||||
|
||||
export function isCurrencyCode(value: string): value is CurrencyCode {
|
||||
return (CURRENCY_CODES as readonly string[]).includes(value);
|
||||
}
|
||||
|
||||
export function currencyCode(value: string): CurrencyCode {
|
||||
assert(isCurrencyCode(value), `Unsupported currency code: ${value}`);
|
||||
return value;
|
||||
}
|
||||
|
||||
const CURRENCY_SCALES = [2] as const;
|
||||
|
||||
/**
|
||||
* Represents the scale of the currency.
|
||||
*
|
||||
* - `2`: The minor unit is 1/100 of the major unit.
|
||||
*/
|
||||
type CurrencyScale = (typeof CURRENCY_SCALES)[number];
|
||||
|
||||
export function isCurrencyScale(value: number): value is CurrencyScale {
|
||||
return (CURRENCY_SCALES as readonly number[]).includes(value);
|
||||
}
|
||||
|
||||
export function currencyScale(value: number): CurrencyScale {
|
||||
assert(isCurrencyScale(value), `Unsupported currency scale: ${value}`);
|
||||
return value;
|
||||
}
|
||||
|
||||
type CurrencyMap = {
|
||||
[K in CurrencyCode]: Currency;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a currency with decimal precision.
|
||||
*
|
||||
* To add support for a currency:
|
||||
* 1. Add the new currency code to the {@link CURRENCY_CODES} array.
|
||||
* 2. Create a corresponding mapping in the {@link SUPPORTED_CURRENCIES} map.
|
||||
*/
|
||||
export class Currency {
|
||||
private static readonly SUPPORTED_CURRENCIES: CurrencyMap = {
|
||||
USD: new Currency('USD', 2),
|
||||
EUR: new Currency('EUR', 2),
|
||||
CAD: new Currency('CAD', 2),
|
||||
GBP: new Currency('GBP', 2),
|
||||
};
|
||||
|
||||
private readonly _code: CurrencyCode;
|
||||
private readonly _scale: CurrencyScale;
|
||||
|
||||
constructor(code: string | CurrencyCode, scale: number | CurrencyScale) {
|
||||
this._code = currencyCode(code);
|
||||
this._scale = currencyScale(scale);
|
||||
}
|
||||
|
||||
get code() {
|
||||
return this._code;
|
||||
}
|
||||
|
||||
get scale() {
|
||||
return this._scale;
|
||||
}
|
||||
|
||||
static of(code: string | CurrencyCode) {
|
||||
assert(isCurrencyCode(code), `Unsupported currency code: ${code}`);
|
||||
const currency = Currency.SUPPORTED_CURRENCIES[code];
|
||||
if (currency === undefined) {
|
||||
throw Error(`${code} currency is not supported`);
|
||||
}
|
||||
return currency;
|
||||
}
|
||||
}
|
||||
118
tools/citrineos-core-main/packages/base/src/money/Money.ts
Normal file
118
tools/citrineos-core-main/packages/base/src/money/Money.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { CurrencyCode } from './Currency.js';
|
||||
import { Currency } from './Currency.js';
|
||||
import { Big } from 'big.js';
|
||||
import { assert, notNull } from '../assertion/assertion.js';
|
||||
|
||||
export type CurrencySource = string | CurrencyCode | Currency;
|
||||
|
||||
export class Money {
|
||||
private readonly _amount: Big;
|
||||
private readonly _currency: Currency;
|
||||
|
||||
private constructor(amount: number | string | Big, currency: CurrencySource) {
|
||||
assert(notNull(amount), 'Amount has to be defined');
|
||||
assert(notNull(currency), 'Currency has to be defined');
|
||||
try {
|
||||
this._amount = new Big(amount);
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid money amount: ${amount}`);
|
||||
}
|
||||
this._currency = typeof currency === 'string' ? Currency.of(currency) : currency;
|
||||
}
|
||||
|
||||
get amount(): Big {
|
||||
return this._amount;
|
||||
}
|
||||
|
||||
get currency(): Currency {
|
||||
return this._currency;
|
||||
}
|
||||
|
||||
static of(amount: number | string | Big, currency: CurrencySource): Money {
|
||||
return new Money(amount, currency);
|
||||
}
|
||||
|
||||
static USD(amount: number | string | Big) {
|
||||
return new Money(amount, 'USD');
|
||||
}
|
||||
|
||||
toNumber(): number {
|
||||
return this._amount.toNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the amount down to match the currency's defined scale.
|
||||
* This method could be used when converting an amount to its final monetary value.
|
||||
*
|
||||
* @returns {Money} A new Money instance with the amount rounded down to the currency's scale.
|
||||
*/
|
||||
roundToCurrencyScale(): Money {
|
||||
const newAmount = this._amount.round(
|
||||
this.currency.scale,
|
||||
0, // RoundDown
|
||||
);
|
||||
return this.withAmount(newAmount);
|
||||
}
|
||||
|
||||
multiply(multiplier: number | string | Big): Money {
|
||||
return this.withAmount(this.amount.times(multiplier));
|
||||
}
|
||||
|
||||
add(money: Money): Money {
|
||||
this.requireSameCurrency(money);
|
||||
return this.withAmount(this.amount.plus(money.amount));
|
||||
}
|
||||
|
||||
subtract(money: Money): Money {
|
||||
this.requireSameCurrency(money);
|
||||
return this.withAmount(this.amount.minus(money.amount));
|
||||
}
|
||||
|
||||
equals(money: Money): boolean {
|
||||
return this._currency === money._currency && this.amount.eq(money.amount);
|
||||
}
|
||||
|
||||
greaterThan(money: Money): boolean {
|
||||
this.requireSameCurrency(money);
|
||||
return this.amount.gt(money.amount);
|
||||
}
|
||||
|
||||
greaterThanOrEqual(money: Money): boolean {
|
||||
this.requireSameCurrency(money);
|
||||
return this.amount.gte(money.amount);
|
||||
}
|
||||
|
||||
lessThan(money: Money): boolean {
|
||||
this.requireSameCurrency(money);
|
||||
return this.amount.lt(money.amount);
|
||||
}
|
||||
|
||||
lessThanOrEqual(money: Money): boolean {
|
||||
this.requireSameCurrency(money);
|
||||
return this.amount.lte(money.amount);
|
||||
}
|
||||
|
||||
isZero(): boolean {
|
||||
return this.amount.eq(0);
|
||||
}
|
||||
|
||||
isPositive(): boolean {
|
||||
return this.amount.gt(0);
|
||||
}
|
||||
|
||||
isNegative(): boolean {
|
||||
return this.amount.lt(0);
|
||||
}
|
||||
|
||||
private withAmount(amount: Big): Money {
|
||||
return new Money(amount, this._currency);
|
||||
}
|
||||
|
||||
private requireSameCurrency(money: Money) {
|
||||
assert(this.currency.code === money.currency.code, 'Currency mismatch');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user