prow-authority 3.6.3
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/.claude/settings.local.json +11 -0
- package/.dockerignore +11 -0
- package/.github/workflows/_deploy.yml +191 -0
- package/.github/workflows/deploy-dev.yml +30 -0
- package/.github/workflows/deploy-prod.yml +34 -0
- package/CLAUDE.md +114 -0
- package/Dockerfile +61 -0
- package/Dockerfile.dev +24 -0
- package/README.md +1 -0
- package/package.json +61 -0
- package/src/App.ts +40 -0
- package/src/helpers/api-error.ts +21 -0
- package/src/helpers/authority.ts +216 -0
- package/src/helpers/config.ts +4 -0
- package/src/helpers/holded.ts +105 -0
- package/src/helpers/mysql.ts +15 -0
- package/src/helpers/notifications.ts +55 -0
- package/src/helpers/random.ts +18 -0
- package/src/helpers/s3.ts +43 -0
- package/src/helpers/stripe.ts +8 -0
- package/src/index.ts +47 -0
- package/src/v1/common-types.ts +12 -0
- package/src/v1/controllers/admin/accounting-seat-controller.ts +148 -0
- package/src/v1/controllers/admin/billing-cycle-controller.ts +171 -0
- package/src/v1/controllers/admin/invoice-controller.ts +222 -0
- package/src/v1/controllers/admin/license-controller.ts +190 -0
- package/src/v1/controllers/admin/license-limit-controller.ts +160 -0
- package/src/v1/controllers/admin/notification-controller.ts +115 -0
- package/src/v1/controllers/admin/organization-controller.ts +210 -0
- package/src/v1/controllers/admin/organization-license-controller.ts +224 -0
- package/src/v1/controllers/admin/organization-license-limit-controller.ts +196 -0
- package/src/v1/controllers/admin/organization-usage-controller.ts +167 -0
- package/src/v1/controllers/admin/payment-method-controller.ts +173 -0
- package/src/v1/controllers/admin/policy-controller.ts +138 -0
- package/src/v1/controllers/admin/policy-version-controller.ts +143 -0
- package/src/v1/controllers/admin/schedule-controller.ts +181 -0
- package/src/v1/controllers/admin/service-controller.ts +206 -0
- package/src/v1/controllers/admin/service-notification-controller.ts +166 -0
- package/src/v1/controllers/admin/user-controller.ts +282 -0
- package/src/v1/controllers/admin/user-organization-controller.ts +172 -0
- package/src/v1/controllers/admin/user-policy-controller.ts +164 -0
- package/src/v1/controllers/auth/@deprecated oauth-controller.ts +191 -0
- package/src/v1/controllers/auth/identity-token-controller.ts +286 -0
- package/src/v1/controllers/auth/organization-token-controller.ts +277 -0
- package/src/v1/controllers/auth/temp-code-controller.ts +60 -0
- package/src/v1/controllers/billing/billing-controller.ts +805 -0
- package/src/v1/controllers/organizations/public-organization-controller.ts +1796 -0
- package/src/v1/controllers/policies/public-policy-controller.ts +68 -0
- package/src/v1/controllers/services/public-service-controller.ts +177 -0
- package/src/v1/controllers/services/service-controller.ts +474 -0
- package/src/v1/controllers/users/public-user-controller.ts +471 -0
- package/src/v1/daos/@deprecated auth-dao.ts +173 -0
- package/src/v1/daos/accounting-seat-dao.ts +111 -0
- package/src/v1/daos/billing-cycle-dao.ts +107 -0
- package/src/v1/daos/invoice-dao.ts +131 -0
- package/src/v1/daos/license-dao.ts +152 -0
- package/src/v1/daos/license-limit-dao.ts +120 -0
- package/src/v1/daos/organization-dao.ts +109 -0
- package/src/v1/daos/organization-license-dao.ts +117 -0
- package/src/v1/daos/organization-license-limit-dao.ts +157 -0
- package/src/v1/daos/organization-usage-dao.ts +115 -0
- package/src/v1/daos/payment-method-dao.ts +110 -0
- package/src/v1/daos/policy-dao.ts +105 -0
- package/src/v1/daos/policy-version-dao.ts +112 -0
- package/src/v1/daos/redis-dao.ts +38 -0
- package/src/v1/daos/schema.sql +462 -0
- package/src/v1/daos/service-dao.ts +118 -0
- package/src/v1/daos/service-notification-dao.ts +120 -0
- package/src/v1/daos/user-dao.ts +127 -0
- package/src/v1/daos/user-organization-dao.ts +123 -0
- package/src/v1/daos/user-policy-dao.ts +109 -0
- package/src/v1/middlewares/auth/identity-token-middleware.ts +51 -0
- package/src/v1/middlewares/auth/organization-token-middleware.ts +73 -0
- package/src/v1/middlewares/auth/service-token-middleware.ts +167 -0
- package/src/v1/middlewares/authority-middleware.ts +97 -0
- package/src/v1/middlewares/organizations/organization-middleware.ts +94 -0
- package/src/v1/middlewares/static/static-middleware.ts +41 -0
- package/src/v1/middlewares/users/user-middleware.ts +41 -0
- package/src/v1/routes/admin/accounting-seat-router.ts +89 -0
- package/src/v1/routes/admin/admin-router.ts +61 -0
- package/src/v1/routes/admin/billing-cycle-router.ts +89 -0
- package/src/v1/routes/admin/invoice-router.ts +89 -0
- package/src/v1/routes/admin/license-limit-router.ts +89 -0
- package/src/v1/routes/admin/license-router.ts +89 -0
- package/src/v1/routes/admin/notification-router.ts +61 -0
- package/src/v1/routes/admin/organization-license-limit-router.ts +89 -0
- package/src/v1/routes/admin/organization-license-router.ts +89 -0
- package/src/v1/routes/admin/organization-router.ts +89 -0
- package/src/v1/routes/admin/payment-method-router.ts +89 -0
- package/src/v1/routes/admin/policy-router.ts +89 -0
- package/src/v1/routes/admin/policy-version-router.ts +89 -0
- package/src/v1/routes/admin/schedule-router.ts +75 -0
- package/src/v1/routes/admin/service-notification-router.ts +98 -0
- package/src/v1/routes/admin/service-router.ts +89 -0
- package/src/v1/routes/admin/user-organization-router.ts +89 -0
- package/src/v1/routes/admin/user-policy-router.ts +89 -0
- package/src/v1/routes/admin/user-router.ts +89 -0
- package/src/v1/routes/auth/@deprecated auth-router.ts +199 -0
- package/src/v1/routes/auth/identity-token-router.ts +147 -0
- package/src/v1/routes/auth/organization-token-router.ts +70 -0
- package/src/v1/routes/billing/billing-router.ts +227 -0
- package/src/v1/routes/organizations/invoices-router.ts +48 -0
- package/src/v1/routes/organizations/payments-router.ts +104 -0
- package/src/v1/routes/organizations/public-organization-router.ts +319 -0
- package/src/v1/routes/organizations/users-router.ts +139 -0
- package/src/v1/routes/policies/public-policy-router.ts +50 -0
- package/src/v1/routes/services/public-service-router.ts +89 -0
- package/src/v1/routes/services/service-router.ts +197 -0
- package/src/v1/routes/users/public-user-router.ts +230 -0
- package/src/v1/routes/v1-router.ts +50 -0
- package/src/v1/schemas/admin/accounting-seat-schemas.ts +22 -0
- package/src/v1/schemas/admin/billing-cycle-schemas.ts +20 -0
- package/src/v1/schemas/admin/invoice-schemas.ts +41 -0
- package/src/v1/schemas/admin/license-limit-schemas.ts +25 -0
- package/src/v1/schemas/admin/license-schemas.ts +30 -0
- package/src/v1/schemas/admin/organization-license-limit-schemas.ts +29 -0
- package/src/v1/schemas/admin/organization-license-schemas.ts +43 -0
- package/src/v1/schemas/admin/organization-schemas.ts +67 -0
- package/src/v1/schemas/admin/organization-usage-schemas.ts +15 -0
- package/src/v1/schemas/admin/payment-method-schemas.ts +40 -0
- package/src/v1/schemas/admin/policy-schemas.ts +31 -0
- package/src/v1/schemas/admin/service-notification-schemas.ts +23 -0
- package/src/v1/schemas/admin/service-schemas.ts +56 -0
- package/src/v1/schemas/admin/user-organization-schemas.ts +24 -0
- package/src/v1/schemas/admin/user-policy-schemas.ts +17 -0
- package/src/v1/schemas/admin/user-schemas.ts +52 -0
- package/src/v1/schemas/auth/auth-schemas.ts +55 -0
- package/src/v1/schemas/billing/billing-schemas.ts +32 -0
- package/src/v1/schemas/organizations/public-organization-schemas.ts +77 -0
- package/src/v1/schemas/payments/payment-method-schemas.ts +0 -0
- package/src/v1/schemas/services/public-service-schemas.ts +5 -0
- package/src/v1/schemas/services/service-schemas.ts +27 -0
- package/src/v1/schemas/users/public-user-schemas.ts +73 -0
- package/src/v1/types/admin/accounting-seat-types.ts +16 -0
- package/src/v1/types/admin/billing-cycle-types.ts +14 -0
- package/src/v1/types/admin/invoice-types.ts +39 -0
- package/src/v1/types/admin/license-limit-types.ts +16 -0
- package/src/v1/types/admin/license-types.ts +29 -0
- package/src/v1/types/admin/organization-license-limit-types.ts +13 -0
- package/src/v1/types/admin/organization-license-types.ts +21 -0
- package/src/v1/types/admin/organization-types.ts +60 -0
- package/src/v1/types/admin/organization-usage-types.ts +17 -0
- package/src/v1/types/admin/payment-method-types.ts +30 -0
- package/src/v1/types/admin/policy-types.ts +9 -0
- package/src/v1/types/admin/policy-version-types.ts +12 -0
- package/src/v1/types/admin/service-notification-types.ts +20 -0
- package/src/v1/types/admin/service-types.ts +36 -0
- package/src/v1/types/admin/user-organization-types.ts +31 -0
- package/src/v1/types/admin/user-types.ts +44 -0
- package/src/v1/types/auth/@deprecated auth-types.ts +26 -0
- package/src/v1/types/auth/common-types.ts +7 -0
- package/src/v1/types/auth/confirmation-types.ts +5 -0
- package/src/v1/types/auth/identity-token-types.ts +8 -0
- package/src/v1/types/auth/organization-token-types.ts +23 -0
- package/src/v1/types/auth/otp-types.ts +12 -0
- package/src/v1/types/billing/billing-types.ts +37 -0
- package/src/v1/types/billing/ue-types.ts +29 -0
- package/src/v1/types/organizations/public-organization-types.ts +76 -0
- package/src/v1/types/policies/public-policy-types.ts +7 -0
- package/src/v1/types/services/public-service-types.ts +20 -0
- package/src/v1/types/services/service-types.ts +43 -0
- package/src/v1/types/users/public-user-types.ts +15 -0
- package/src/v1/types/users/user-policy-types.ts +11 -0
- package/static/error.html +55 -0
- package/static/grant.html +117 -0
- package/static/login.html +54 -0
- package/static/oauth-client-logo.webp +0 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { Date2, Formats } from "@beseif-solutions/utility-functions/dist/utils/date-utils";
|
|
2
|
+
import { JSON2 } from "@beseif-solutions/utility-functions/dist/utils/json-utils";
|
|
3
|
+
import { Where } from "@beseif-solutions/utility-functions/dist/utils/sql-utils";
|
|
4
|
+
import { newUUIDv4 } from "@beseif-solutions/utility-functions/dist/utils/string-utils";
|
|
5
|
+
import { DeepPartial } from "@beseif-solutions/utility-functions/dist/utils/typing-utils";
|
|
6
|
+
import { Validator } from "@beseif-solutions/utility-functions/dist/utils/validation-utils";
|
|
7
|
+
import APIError, { StatusCodes } from "../../../helpers/api-error";
|
|
8
|
+
import billingCycleDao from "../../daos/billing-cycle-dao";
|
|
9
|
+
import { addBillingCycleSchema, updateBillingCycleSchema } from "../../schemas/admin/billing-cycle-schemas";
|
|
10
|
+
import { BillingCycle, DBRecord } from "../../types/admin/billing-cycle-types";
|
|
11
|
+
import { OrganizationStatus } from "../../types/admin/organization-types";
|
|
12
|
+
import organizationController from "./organization-controller";
|
|
13
|
+
import { Modifiers } from "../../common-types";
|
|
14
|
+
|
|
15
|
+
type Add = Omit<BillingCycle, `id` | `uuid`>;
|
|
16
|
+
type Update = DeepPartial<Omit<BillingCycle, `id` | `uuid`>>;
|
|
17
|
+
|
|
18
|
+
class BillingCycleCtrl {
|
|
19
|
+
|
|
20
|
+
public readonly get = async (id: number): Promise<BillingCycle[]> => {
|
|
21
|
+
try {
|
|
22
|
+
const db = await billingCycleDao.get({ id: id });
|
|
23
|
+
const response = await this.parse(db);
|
|
24
|
+
return response;
|
|
25
|
+
} catch (e) { throw e; }
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
public readonly list = async (params: Where<BillingCycle>, options: Modifiers = {}): Promise<BillingCycle[]> => {
|
|
29
|
+
try {
|
|
30
|
+
const db = await billingCycleDao.list({ ...params }, options);
|
|
31
|
+
const response = await this.parse(db);
|
|
32
|
+
return response;
|
|
33
|
+
} catch (e) {
|
|
34
|
+
throw e;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
public readonly add = async (data: Add): Promise<BillingCycle[]> => {
|
|
39
|
+
try {
|
|
40
|
+
await Validator.validateAsync(addBillingCycleSchema, data);
|
|
41
|
+
await this.check(data);
|
|
42
|
+
|
|
43
|
+
const uuid = await this.getFreeUUID();
|
|
44
|
+
|
|
45
|
+
const { insertId } = await billingCycleDao.add({
|
|
46
|
+
uuid: uuid,
|
|
47
|
+
organization: data.organization,
|
|
48
|
+
trial: data.trial,
|
|
49
|
+
start_date: data.start_date,
|
|
50
|
+
end_date: data.end_date,
|
|
51
|
+
metadata: data.metadata,
|
|
52
|
+
internal: data.internal,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return await this.get(insertId);
|
|
56
|
+
} catch (e) { throw e; }
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
public readonly update = async (id: number, data: Update): Promise<BillingCycle[]> => {
|
|
60
|
+
try {
|
|
61
|
+
await Validator.validateAsync(updateBillingCycleSchema, data);
|
|
62
|
+
|
|
63
|
+
const old = (await this.get(id))[0];
|
|
64
|
+
if (!old) {
|
|
65
|
+
throw new APIError({
|
|
66
|
+
code: StatusCodes.NOT_FOUND,
|
|
67
|
+
detail: `Billing cycle with id ${id} not found.`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const merged = JSON2.deepClean(JSON2.deepMerge({}, old, data)) as BillingCycle;
|
|
72
|
+
|
|
73
|
+
await this.check(merged, old);
|
|
74
|
+
|
|
75
|
+
const { affectedRows } = await billingCycleDao.update({ id: id }, {
|
|
76
|
+
uuid: merged.uuid,
|
|
77
|
+
organization: merged.organization,
|
|
78
|
+
trial: merged.trial,
|
|
79
|
+
start_date: merged.start_date,
|
|
80
|
+
end_date: merged.end_date,
|
|
81
|
+
metadata: merged.metadata,
|
|
82
|
+
internal: merged.internal,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (affectedRows <= 0) {
|
|
86
|
+
throw new APIError({
|
|
87
|
+
code: StatusCodes.INTERNAL_SERVER_ERROR,
|
|
88
|
+
detail: `Could not update billing cycle with id ${id}`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return await this.get(id);
|
|
93
|
+
} catch (e) { throw e; }
|
|
94
|
+
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
public readonly delete = async (id: number): Promise<{ deleted: boolean }> => {
|
|
98
|
+
try {
|
|
99
|
+
const billingCycle = (await this.get(id))[0];
|
|
100
|
+
if (!billingCycle) {
|
|
101
|
+
throw new APIError({
|
|
102
|
+
code: StatusCodes.NOT_FOUND,
|
|
103
|
+
detail: `Billing cycle with id ${id} not found.`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const { affectedRows } = await billingCycleDao.delete({ id: billingCycle.id });
|
|
108
|
+
|
|
109
|
+
if (affectedRows <= 0) {
|
|
110
|
+
throw new APIError({
|
|
111
|
+
code: StatusCodes.INTERNAL_SERVER_ERROR,
|
|
112
|
+
detail: `Could not delete billing cycle with id ${id}`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { deleted: affectedRows > 0 };
|
|
117
|
+
} catch (e) { throw e; }
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
private readonly getFreeUUID = async (): Promise<string> => {
|
|
121
|
+
try {
|
|
122
|
+
let uuid: string;
|
|
123
|
+
do {
|
|
124
|
+
uuid = newUUIDv4({ case: `lower` });
|
|
125
|
+
const exists = (await billingCycleDao.list({ uuid: uuid }))[0];
|
|
126
|
+
if (!exists) { break; }
|
|
127
|
+
} while (true);
|
|
128
|
+
return uuid;
|
|
129
|
+
} catch (e) { throw e; }
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
private readonly check = async (data: Add | BillingCycle, old?: BillingCycle): Promise<void> => {
|
|
133
|
+
try {
|
|
134
|
+
if (!old || data.organization !== old.organization) {
|
|
135
|
+
const organization = (await organizationController.list({ id: data.organization, status: { is: OrganizationStatus.Blocked, inverse: true } }))[0];
|
|
136
|
+
if (!organization) {
|
|
137
|
+
throw new APIError({
|
|
138
|
+
code: StatusCodes.BAD_REQUEST,
|
|
139
|
+
detail: `No active organization ${data.organization} found.`,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} catch (e) { throw e; }
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
private readonly parse = async (records: DBRecord[]): Promise<BillingCycle[]> => {
|
|
147
|
+
try {
|
|
148
|
+
const response: BillingCycle[] = [];
|
|
149
|
+
|
|
150
|
+
for (const record of records) {
|
|
151
|
+
const p: BillingCycle = {
|
|
152
|
+
id: record.id,
|
|
153
|
+
uuid: record.uuid,
|
|
154
|
+
organization: record.organization,
|
|
155
|
+
trial: (record.trial as any) === 1,
|
|
156
|
+
start_date: new Date2(record.start_date).format(Formats.ISO),
|
|
157
|
+
end_date: new Date2(record.end_date).format(Formats.ISO),
|
|
158
|
+
metadata: record.metadata,
|
|
159
|
+
internal: record.internal,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
response.push(p);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return response;
|
|
166
|
+
} catch (e) { throw e; }
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const billingCycleController = new BillingCycleCtrl();
|
|
171
|
+
export default billingCycleController;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { Date2, Formats } from "@beseif-solutions/utility-functions/dist/utils/date-utils";
|
|
2
|
+
import { JSON2 } from "@beseif-solutions/utility-functions/dist/utils/json-utils";
|
|
3
|
+
import { Where } from "@beseif-solutions/utility-functions/dist/utils/sql-utils";
|
|
4
|
+
import { newUUIDv4 } from "@beseif-solutions/utility-functions/dist/utils/string-utils";
|
|
5
|
+
import { DeepPartial } from "@beseif-solutions/utility-functions/dist/utils/typing-utils";
|
|
6
|
+
import { Validator } from "@beseif-solutions/utility-functions/dist/utils/validation-utils";
|
|
7
|
+
import APIError, { StatusCodes } from "../../../helpers/api-error";
|
|
8
|
+
import invoiceDao from "../../daos/invoice-dao";
|
|
9
|
+
import { addInvoiceSchema, updateInvoiceSchema } from "../../schemas/admin/invoice-schemas";
|
|
10
|
+
import { DBRecord, Invoice } from "../../types/admin/invoice-types";
|
|
11
|
+
import { OrganizationStatus } from "../../types/admin/organization-types";
|
|
12
|
+
import organizationController from "./organization-controller";
|
|
13
|
+
import paymentMethodController from "./payment-method-controller";
|
|
14
|
+
import _ from "lodash";
|
|
15
|
+
import { Modifiers } from "../../common-types";
|
|
16
|
+
|
|
17
|
+
type Add = Omit<Invoice, `id` | `uuid` | `amount` | `tax` | `base`>;
|
|
18
|
+
type Update = DeepPartial<Omit<Invoice, `id` | `uuid` | `amount` | `tax` | `base`>>;
|
|
19
|
+
|
|
20
|
+
class InvoiceCtrl {
|
|
21
|
+
|
|
22
|
+
public readonly get = async (id: number): Promise<Invoice[]> => {
|
|
23
|
+
try {
|
|
24
|
+
const db = await invoiceDao.get({ id: id });
|
|
25
|
+
const response = await this.parse(db);
|
|
26
|
+
return response;
|
|
27
|
+
} catch (e) { throw e; }
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
public readonly list = async (params: Where<Invoice>, options: Modifiers = {}): Promise<Invoice[]> => {
|
|
31
|
+
try {
|
|
32
|
+
const db = await invoiceDao.list({ ...params }, options);
|
|
33
|
+
const response = await this.parse(db);
|
|
34
|
+
return response;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
throw e;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
public readonly add = async (data: Add): Promise<Invoice[]> => {
|
|
41
|
+
try {
|
|
42
|
+
await Validator.validateAsync(addInvoiceSchema, data);
|
|
43
|
+
await this.check(data);
|
|
44
|
+
|
|
45
|
+
const uuid = await this.getFreeUUID();
|
|
46
|
+
|
|
47
|
+
const { insertId } = await invoiceDao.add({
|
|
48
|
+
uuid: uuid,
|
|
49
|
+
organization: data.organization,
|
|
50
|
+
date: data.date,
|
|
51
|
+
amount: this.getAmount(data),
|
|
52
|
+
tax_code: data.tax_code,
|
|
53
|
+
tax_percentage: data.tax_percentage,
|
|
54
|
+
concepts: data.concepts,
|
|
55
|
+
billing: data.billing,
|
|
56
|
+
status: data.status,
|
|
57
|
+
invoice: data.invoice,
|
|
58
|
+
number: data.number,
|
|
59
|
+
payment: data.payment,
|
|
60
|
+
method: data.method,
|
|
61
|
+
metadata: data.metadata,
|
|
62
|
+
internal: data.internal,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return await this.get(insertId);
|
|
66
|
+
} catch (e) { throw e; }
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
public readonly update = async (id: number, data: Update): Promise<Invoice[]> => {
|
|
70
|
+
try {
|
|
71
|
+
await Validator.validateAsync(updateInvoiceSchema, data);
|
|
72
|
+
|
|
73
|
+
const old = (await this.get(id))[0];
|
|
74
|
+
if (!old) {
|
|
75
|
+
throw new APIError({
|
|
76
|
+
code: StatusCodes.NOT_FOUND,
|
|
77
|
+
detail: `Billing cycle with id ${id} not found.`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const merged = JSON2.deepClean(JSON2.deepMerge({}, old, data)) as Invoice;
|
|
82
|
+
|
|
83
|
+
await this.check(merged, old);
|
|
84
|
+
|
|
85
|
+
const { affectedRows } = await invoiceDao.update({ id: id }, {
|
|
86
|
+
uuid: merged.uuid,
|
|
87
|
+
organization: merged.organization,
|
|
88
|
+
date: merged.date,
|
|
89
|
+
amount: this.getAmount(merged),
|
|
90
|
+
tax_code: merged.tax_code,
|
|
91
|
+
tax_percentage: merged.tax_percentage,
|
|
92
|
+
concepts: merged.concepts,
|
|
93
|
+
billing: merged.billing,
|
|
94
|
+
status: merged.status,
|
|
95
|
+
invoice: merged.invoice,
|
|
96
|
+
number: merged.number,
|
|
97
|
+
payment: merged.payment,
|
|
98
|
+
method: merged.method,
|
|
99
|
+
metadata: merged.metadata,
|
|
100
|
+
internal: merged.internal,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (affectedRows <= 0) {
|
|
104
|
+
throw new APIError({
|
|
105
|
+
code: StatusCodes.INTERNAL_SERVER_ERROR,
|
|
106
|
+
detail: `Could not update billing cycle with id ${id}`,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return await this.get(id);
|
|
111
|
+
} catch (e) { throw e; }
|
|
112
|
+
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
public readonly delete = async (id: number): Promise<{ deleted: boolean }> => {
|
|
116
|
+
try {
|
|
117
|
+
const invoice = (await this.get(id))[0];
|
|
118
|
+
if (!invoice) {
|
|
119
|
+
throw new APIError({
|
|
120
|
+
code: StatusCodes.NOT_FOUND,
|
|
121
|
+
detail: `Billing cycle with id ${id} not found.`,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const { affectedRows } = await invoiceDao.delete({ id: invoice.id });
|
|
126
|
+
|
|
127
|
+
if (affectedRows <= 0) {
|
|
128
|
+
throw new APIError({
|
|
129
|
+
code: StatusCodes.INTERNAL_SERVER_ERROR,
|
|
130
|
+
detail: `Could not delete billing cycle with id ${id}`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return { deleted: affectedRows > 0 };
|
|
135
|
+
} catch (e) { throw e; }
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
private readonly getFreeUUID = async (): Promise<string> => {
|
|
139
|
+
try {
|
|
140
|
+
let uuid: string;
|
|
141
|
+
do {
|
|
142
|
+
uuid = newUUIDv4({ case: `lower` });
|
|
143
|
+
const exists = (await invoiceDao.list({ uuid: uuid }))[0];
|
|
144
|
+
if (!exists) { break; }
|
|
145
|
+
} while (true);
|
|
146
|
+
return uuid;
|
|
147
|
+
} catch (e) { throw e; }
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
private readonly PERCENT = 100;
|
|
151
|
+
private readonly getAmount = (invoice: Pick<Invoice, `concepts` | `tax_percentage`>): number => {
|
|
152
|
+
try {
|
|
153
|
+
const base = _.sum(invoice.concepts.map((c) => c.price));
|
|
154
|
+
const tax = (invoice.tax_percentage / this.PERCENT) * base;
|
|
155
|
+
return base + tax;
|
|
156
|
+
} catch (e) { throw e; }
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
private readonly check = async (data: Add | Invoice, old?: Invoice): Promise<void> => {
|
|
160
|
+
try {
|
|
161
|
+
if (!old || data.organization !== old.organization) {
|
|
162
|
+
const organization = (await organizationController.list({ id: data.organization, status: { is: OrganizationStatus.Blocked, inverse: true } }))[0];
|
|
163
|
+
if (!organization) {
|
|
164
|
+
throw new APIError({
|
|
165
|
+
code: StatusCodes.BAD_REQUEST,
|
|
166
|
+
detail: `No active organization ${data.organization} found.`,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (data.method && (!old || data.method !== old.method)) {
|
|
172
|
+
const method = (await paymentMethodController.list({ id: data.method, organization: data.organization }))[0];
|
|
173
|
+
if (!method) {
|
|
174
|
+
throw new APIError({
|
|
175
|
+
code: StatusCodes.BAD_REQUEST,
|
|
176
|
+
detail: `No active method ${data.method} found.`,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch (e) { throw e; }
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
private readonly parse = async (records: DBRecord[]): Promise<Invoice[]> => {
|
|
184
|
+
try {
|
|
185
|
+
const response: Invoice[] = [];
|
|
186
|
+
|
|
187
|
+
for (const record of records) {
|
|
188
|
+
|
|
189
|
+
const total = parseFloat(record.amount as any);
|
|
190
|
+
const base = _.sum(record.concepts.map((c) => c.price));
|
|
191
|
+
|
|
192
|
+
const p: Invoice = {
|
|
193
|
+
id: record.id,
|
|
194
|
+
uuid: record.uuid,
|
|
195
|
+
organization: record.organization,
|
|
196
|
+
date: new Date2(record.date).format(Formats.ISO),
|
|
197
|
+
base: base,
|
|
198
|
+
tax_code: record.tax_code,
|
|
199
|
+
tax_percentage: parseFloat(record.tax_percentage as any),
|
|
200
|
+
tax: total - base,
|
|
201
|
+
amount: total,
|
|
202
|
+
concepts: record.concepts,
|
|
203
|
+
billing: record.billing,
|
|
204
|
+
status: record.status,
|
|
205
|
+
invoice: record.invoice,
|
|
206
|
+
number: record.number,
|
|
207
|
+
payment: record.payment,
|
|
208
|
+
method: record.method,
|
|
209
|
+
metadata: record.metadata,
|
|
210
|
+
internal: record.internal,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
response.push(p);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return response;
|
|
217
|
+
} catch (e) { throw e; }
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const invoiceController = new InvoiceCtrl();
|
|
222
|
+
export default invoiceController;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { Date2, Formats } from "@beseif-solutions/utility-functions/dist/utils/date-utils";
|
|
2
|
+
import { JSON2 } from "@beseif-solutions/utility-functions/dist/utils/json-utils";
|
|
3
|
+
import { Where } from "@beseif-solutions/utility-functions/dist/utils/sql-utils";
|
|
4
|
+
import { newUUIDv4 } from "@beseif-solutions/utility-functions/dist/utils/string-utils";
|
|
5
|
+
import { DeepPartial } from "@beseif-solutions/utility-functions/dist/utils/typing-utils";
|
|
6
|
+
import { Validator } from "@beseif-solutions/utility-functions/dist/utils/validation-utils";
|
|
7
|
+
import APIError, { StatusCodes } from "../../../helpers/api-error";
|
|
8
|
+
import licenseDao from "../../daos/license-dao";
|
|
9
|
+
import { addLicenseSchema, updateLicenseSchema } from "../../schemas/admin/license-schemas";
|
|
10
|
+
import { DBRecord, License, Prices } from "../../types/admin/license-types";
|
|
11
|
+
import serviceController from "./service-controller";
|
|
12
|
+
import { Modifiers } from "../../common-types";
|
|
13
|
+
|
|
14
|
+
type Add = Omit<License, `id` | keyof Prices | `created_date` | `modified_date`>;
|
|
15
|
+
type Update = DeepPartial<Omit<License, `id` | keyof Prices | `created_date` | `modified_date`>>;
|
|
16
|
+
|
|
17
|
+
class LicenseCtrl {
|
|
18
|
+
|
|
19
|
+
public readonly get = async (id: number): Promise<License[]> => {
|
|
20
|
+
try {
|
|
21
|
+
const db = await licenseDao.get({ id: id });
|
|
22
|
+
const response = await this.parse(db);
|
|
23
|
+
return response;
|
|
24
|
+
} catch (e) { throw e; }
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
public readonly list = async (params: Where<License>, options: Modifiers = {}): Promise<License[]> => {
|
|
28
|
+
try {
|
|
29
|
+
const db = await licenseDao.list({ ...params } as any, options);
|
|
30
|
+
const response = await this.parse(db);
|
|
31
|
+
return response;
|
|
32
|
+
} catch (e) {
|
|
33
|
+
throw e;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
public readonly add = async (data: Add): Promise<License[]> => {
|
|
38
|
+
try {
|
|
39
|
+
await Validator.validateAsync(addLicenseSchema, data);
|
|
40
|
+
await this.check(data);
|
|
41
|
+
|
|
42
|
+
const uuid = await this.getFreeUUID();
|
|
43
|
+
|
|
44
|
+
const { insertId } = await licenseDao.add({
|
|
45
|
+
uuid: uuid,
|
|
46
|
+
service: data.service,
|
|
47
|
+
type: data.type,
|
|
48
|
+
associated: data.associated,
|
|
49
|
+
properties: data.properties,
|
|
50
|
+
data: data.data,
|
|
51
|
+
public: data.public,
|
|
52
|
+
enabled: data.enabled,
|
|
53
|
+
metadata: data.metadata,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return await this.get(insertId);
|
|
57
|
+
} catch (e) { throw e; }
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
public readonly update = async (id: number, data: Update): Promise<License[]> => {
|
|
61
|
+
try {
|
|
62
|
+
await Validator.validateAsync(updateLicenseSchema, data);
|
|
63
|
+
|
|
64
|
+
const old = (await this.get(id))[0];
|
|
65
|
+
if (!old) {
|
|
66
|
+
throw new APIError({
|
|
67
|
+
code: StatusCodes.NOT_FOUND,
|
|
68
|
+
detail: `License with id ${id} not found.`,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const merged = JSON2.deepClean(JSON2.deepMerge({}, old, data)) as License;
|
|
73
|
+
|
|
74
|
+
await this.check(merged, old);
|
|
75
|
+
|
|
76
|
+
const { affectedRows } = await licenseDao.update({ id: id }, {
|
|
77
|
+
uuid: merged.uuid,
|
|
78
|
+
service: merged.service,
|
|
79
|
+
type: merged.type,
|
|
80
|
+
associated: merged.associated,
|
|
81
|
+
properties: merged.properties,
|
|
82
|
+
data: merged.data,
|
|
83
|
+
public: merged.public,
|
|
84
|
+
enabled: merged.enabled,
|
|
85
|
+
metadata: merged.metadata,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (affectedRows <= 0) {
|
|
89
|
+
throw new APIError({
|
|
90
|
+
code: StatusCodes.INTERNAL_SERVER_ERROR,
|
|
91
|
+
detail: `Could not update license with id ${id}`,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return await this.get(id);
|
|
96
|
+
} catch (e) { throw e; }
|
|
97
|
+
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
public readonly delete = async (id: number): Promise<{ deleted: boolean }> => {
|
|
101
|
+
try {
|
|
102
|
+
const license = (await this.get(id))[0];
|
|
103
|
+
if (!license) {
|
|
104
|
+
throw new APIError({
|
|
105
|
+
code: StatusCodes.NOT_FOUND,
|
|
106
|
+
detail: `License with id ${id} not found.`,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const { affectedRows } = await licenseDao.delete({ id: license.id });
|
|
111
|
+
|
|
112
|
+
if (affectedRows <= 0) {
|
|
113
|
+
throw new APIError({
|
|
114
|
+
code: StatusCodes.INTERNAL_SERVER_ERROR,
|
|
115
|
+
detail: `Could not delete license with id ${id}`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { deleted: affectedRows > 0 };
|
|
120
|
+
} catch (e) { throw e; }
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
private readonly getFreeUUID = async (): Promise<string> => {
|
|
124
|
+
try {
|
|
125
|
+
let uuid: string;
|
|
126
|
+
do {
|
|
127
|
+
uuid = newUUIDv4({ case: `lower` });
|
|
128
|
+
const exists = (await licenseDao.list({ uuid: uuid }))[0];
|
|
129
|
+
if (!exists) { break; }
|
|
130
|
+
} while (true);
|
|
131
|
+
return uuid;
|
|
132
|
+
} catch (e) { throw e; }
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
private readonly check = async (data: Add | License, old?: License): Promise<void> => {
|
|
136
|
+
try {
|
|
137
|
+
if (!old || data.service !== old.service) {
|
|
138
|
+
const service = (await serviceController.list({ id: data.service, enabled: true }))[0];
|
|
139
|
+
if (!service) {
|
|
140
|
+
throw new APIError({
|
|
141
|
+
code: StatusCodes.BAD_REQUEST,
|
|
142
|
+
detail: `No enabled service ${data.service} found.`,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!old || (data.associated && data.associated !== old.associated)) {
|
|
148
|
+
const associatedLicense = (await licenseDao.list({ id: data.associated }))[0];
|
|
149
|
+
if (!associatedLicense) {
|
|
150
|
+
throw new APIError({
|
|
151
|
+
code: StatusCodes.BAD_REQUEST,
|
|
152
|
+
detail: `No enabled associated license with id ${data.associated} found.`,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} catch (e) { throw e; }
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
private readonly parse = async (records: DBRecord[]): Promise<License[]> => {
|
|
160
|
+
try {
|
|
161
|
+
const response: License[] = [];
|
|
162
|
+
|
|
163
|
+
for (const record of records) {
|
|
164
|
+
const p: License = {
|
|
165
|
+
id: record.id,
|
|
166
|
+
uuid: record.uuid,
|
|
167
|
+
service: record.service,
|
|
168
|
+
type: record.type,
|
|
169
|
+
associated: record.associated || null,
|
|
170
|
+
properties: record.properties,
|
|
171
|
+
data: record.data,
|
|
172
|
+
created_date: new Date2(record.created_date).format(Formats.ISO),
|
|
173
|
+
modified_date: new Date2(record.modified_date).format(Formats.ISO),
|
|
174
|
+
public: record.public,
|
|
175
|
+
annual_price: parseFloat(record.annual_price || `0`),
|
|
176
|
+
monthly_price: parseFloat(record.monthly_price || `0`),
|
|
177
|
+
enabled: record.enabled ? true : false,
|
|
178
|
+
metadata: record.metadata,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
response.push(p);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return response;
|
|
185
|
+
} catch (e) { throw e; }
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const licenseController = new LicenseCtrl();
|
|
190
|
+
export default licenseController;
|