pangea-server 3.3.144 → 3.3.145

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.
@@ -1,4 +1,4 @@
1
- import type { ArcaConfig, ArcaVoucherType, ArcaVoucherInfo, ArcaCreateInvoiceCOptions, ArcaCreateVoucherRes } from '../types/arca.types';
1
+ import type { ArcaConfig, ArcaVoucherType, ArcaVoucherInfo, ArcaCreateInvoiceCOptions, ArcaCreateInvoiceCPdfOptions, ArcaCreateVoucherRes } from '../types/arca.types';
2
2
  export declare class Arca {
3
3
  private __cuit;
4
4
  private __issuer;
@@ -7,4 +7,5 @@ export declare class Arca {
7
7
  getLastVoucher(pointOfSale: number, voucherType: ArcaVoucherType): Promise<number>;
8
8
  getVoucherInfo(voucherNumber: number, pointOfSale: number, voucherType: ArcaVoucherType): Promise<ArcaVoucherInfo | null>;
9
9
  createInvoiceC(options: ArcaCreateInvoiceCOptions): Promise<ArcaCreateVoucherRes>;
10
+ createInvoiceCPdf(options: ArcaCreateInvoiceCPdfOptions): Promise<string>;
10
11
  }
@@ -100,20 +100,19 @@ class Arca {
100
100
  };
101
101
  }
102
102
  async createInvoiceC(options) {
103
- const { pointOfSale, receiver, concept, items, amount, service } = options;
103
+ const { pointOfSale, receiver, concept, amount, service } = options;
104
104
  const voucherType = '11_invoice_c';
105
105
  const isConceptProducts = concept === '1_products';
106
106
  if (!isConceptProducts && !service)
107
107
  throw new Error('Service dates are required');
108
- const isFinalConsumer = receiver.documentType === '99_final_consumer';
109
- const receiverDocumentNumber = isFinalConsumer ? 0 : receiver.documentNumber || 0;
110
- const receiverVatCondition = receiver.vatCondition || '5_final_consumer';
111
- const receiverName = receiver.name || 'CONSUMIDOR FINAL';
108
+ const { receiverDocumentNumber, receiverVatCondition } = getReceiverFields(receiver);
112
109
  const lastVoucherNumber = await this.getLastVoucher(pointOfSale, voucherType);
113
110
  const voucherNumber = lastVoucherNumber + 1;
114
- const timezone = -3;
115
- const localDate = new Date(Date.now() + timezone * 60 * 60000);
116
- const voucherDate = Number(localDate.toISOString().split('T')[0].replace(/-/g, ''));
111
+ const localDate = getLocalDate();
112
+ const voucherDate = getArcaDate(localDate.toISOString().split('T')[0]);
113
+ const day = String(localDate.getDate()).padStart(2, '0');
114
+ const month = String(localDate.getMonth() + 1).padStart(2, '0');
115
+ const issueDate = `${localDate.getFullYear()}-${month}-${day}`;
117
116
  const params = {
118
117
  PtoVta: pointOfSale,
119
118
  CbteTipo: voucherTypeIds[voucherType],
@@ -133,78 +132,18 @@ class Arca {
133
132
  ImpTrib: 0,
134
133
  MonId: 'PES',
135
134
  MonCotiz: 1,
136
- FchServDesde: isConceptProducts ? null : Number(service.from.replace(/-/g, '')),
137
- FchServHasta: isConceptProducts ? null : Number(service.to.replace(/-/g, '')),
138
- FchVtoPago: isConceptProducts ? null : Number(service.paymentDueDate.replace(/-/g, '')),
135
+ FchServDesde: isConceptProducts ? null : getArcaDate(service.from),
136
+ FchServHasta: isConceptProducts ? null : getArcaDate(service.to),
137
+ FchVtoPago: isConceptProducts ? null : getArcaDate(service.paymentDueDate),
139
138
  };
140
139
  try {
141
140
  const res = await this.__afip.ElectronicBilling.createVoucher(params);
142
- const finalRes = { voucherNumber, cae: res.CAE, caeExpirationDate: res.CAEFchVto };
143
- try {
144
- const day = String(localDate.getDate()).padStart(2, '0');
145
- const month = String(localDate.getMonth() + 1).padStart(2, '0');
146
- const year = localDate.getFullYear();
147
- const issueDate = `${day}/${month}/${year}`;
148
- const caeDateStr = res.CAEFchVto.replace(/-/g, '');
149
- const caeDueDate = `${caeDateStr.slice(6, 8)}/${caeDateStr.slice(4, 6)}/${caeDateStr.slice(0, 4)}`;
150
- const timestamp = localDate
151
- .toISOString()
152
- .replace(/[-:T.]/g, '')
153
- .slice(0, 14);
154
- const clientName = receiverName.replace(/\s+/g, '_').toLowerCase();
155
- const fileName = `${timestamp}_${clientName}.pdf`;
156
- const pdfData = {
157
- file_name: fileName,
158
- send_to: receiver.email || undefined,
159
- template: {
160
- name: 'invoice-c',
161
- params: {
162
- voucher_number: voucherNumber,
163
- sales_point: pointOfSale,
164
- issue_date: issueDate,
165
- cae_due_date: caeDueDate,
166
- issuer_cuit: this.__cuit,
167
- cae: res.CAE,
168
- issuer_business_name: this.__issuer.businessName,
169
- issuer_address: this.__issuer.address,
170
- issuer_iva_condition: this.__issuer.vatCondition,
171
- issuer_gross_income: this.__issuer.grossIncome,
172
- issuer_activity_start_date: this.__issuer.activityStartDate.split('-').reverse().join('/'),
173
- receiver_name: receiverName,
174
- receiver_address: receiver.address || '-',
175
- receiver_document_type: documentTypeIds[receiver.documentType],
176
- receiver_document_number: receiverDocumentNumber,
177
- receiver_iva_condition: vatConditionReceiverDescriptions[receiverVatCondition],
178
- sale_condition: 'Contado',
179
- currency_id: 'ARS',
180
- currency_rate: 1,
181
- concept: conceptIds[concept],
182
- items: items.map((item) => ({
183
- code: item.code,
184
- description: item.description,
185
- quantity: item.quantity,
186
- unit_price: item.price,
187
- subtotal: item.quantity * item.price,
188
- })),
189
- vat_amount: 0,
190
- tributes_amount: 0,
191
- total_amount: amount,
192
- billing_from: isConceptProducts ? undefined : service.from.split('-').reverse().join('/'),
193
- billing_to: isConceptProducts ? undefined : service.to.split('-').reverse().join('/'),
194
- payment_due_date: isConceptProducts ? undefined : service.paymentDueDate.split('-').reverse().join('/'),
195
- net_amount_taxed: amount,
196
- net_amount_untaxed: 0,
197
- exempt_amount: 0,
198
- },
199
- },
200
- };
201
- const pdfRes = await this.__afip.ElectronicBilling.createPDF(pdfData);
202
- finalRes.file = pdfRes.file;
203
- }
204
- catch (pdfErr) {
205
- (0, helpers_1.printDanger)('arca', 'Error creating PDF (voucher was created)');
206
- console.log(JSON.stringify(pdfErr, null, 2));
207
- }
141
+ const finalRes = {
142
+ voucherNumber,
143
+ issueDate,
144
+ cae: res.CAE,
145
+ caeExpirationDate: res.CAEFchVto,
146
+ };
208
147
  (0, helpers_1.printSuccess)('arca', 'Voucher created successfully');
209
148
  console.log('params:');
210
149
  console.log(params);
@@ -220,5 +159,91 @@ class Arca {
220
159
  helpers_1.AppError.Throw({ statusCodeName: 'INTERNAL_SERVER_ERROR', errorCode: 'VOUCHER_CREATION_FAILED' });
221
160
  }
222
161
  }
162
+ async createInvoiceCPdf(options) {
163
+ const { pointOfSale, receiver, concept, items, amount, service, voucherNumber, issueDate, cae, caeExpirationDate } = options;
164
+ const isConceptProducts = concept === '1_products';
165
+ const { receiverDocumentNumber, receiverVatCondition } = getReceiverFields(receiver);
166
+ const receiverName = receiver.name || 'CONSUMIDOR FINAL';
167
+ const caeDateStr = caeExpirationDate.replace(/-/g, '');
168
+ const caeDueDate = `${caeDateStr.slice(6, 8)}/${caeDateStr.slice(4, 6)}/${caeDateStr.slice(0, 4)}`;
169
+ const timestamp = getLocalDate()
170
+ .toISOString()
171
+ .replace(/[-:T.]/g, '')
172
+ .slice(0, 14);
173
+ const clientName = receiverName.replace(/\s+/g, '_').toLowerCase();
174
+ const fileName = `${timestamp}_${clientName}.pdf`;
175
+ const pdfData = {
176
+ file_name: fileName,
177
+ send_to: receiver.email || undefined,
178
+ template: {
179
+ name: 'invoice-c',
180
+ params: {
181
+ voucher_number: voucherNumber,
182
+ sales_point: pointOfSale,
183
+ issue_date: formatDateSlash(issueDate),
184
+ cae_due_date: caeDueDate,
185
+ issuer_cuit: this.__cuit,
186
+ cae,
187
+ issuer_business_name: this.__issuer.businessName,
188
+ issuer_address: this.__issuer.address,
189
+ issuer_iva_condition: this.__issuer.vatCondition,
190
+ issuer_gross_income: this.__issuer.grossIncome,
191
+ issuer_activity_start_date: formatDateSlash(this.__issuer.activityStartDate),
192
+ receiver_name: receiverName,
193
+ receiver_address: receiver.address || '-',
194
+ receiver_document_type: documentTypeIds[receiver.documentType],
195
+ receiver_document_number: receiverDocumentNumber,
196
+ receiver_iva_condition: vatConditionReceiverDescriptions[receiverVatCondition],
197
+ sale_condition: 'Contado',
198
+ currency_id: 'ARS',
199
+ currency_rate: 1,
200
+ concept: conceptIds[concept],
201
+ items: items.map((item) => ({
202
+ code: item.code,
203
+ description: item.description,
204
+ quantity: item.quantity,
205
+ unit_price: item.price,
206
+ subtotal: item.quantity * item.price,
207
+ })),
208
+ vat_amount: 0,
209
+ tributes_amount: 0,
210
+ total_amount: amount,
211
+ billing_from: isConceptProducts ? undefined : formatDateSlash(service.from),
212
+ billing_to: isConceptProducts ? undefined : formatDateSlash(service.to),
213
+ payment_due_date: isConceptProducts ? undefined : formatDateSlash(service.paymentDueDate),
214
+ net_amount_taxed: amount,
215
+ net_amount_untaxed: 0,
216
+ exempt_amount: 0,
217
+ },
218
+ },
219
+ };
220
+ try {
221
+ const pdfRes = await this.__afip.ElectronicBilling.createPDF(pdfData);
222
+ (0, helpers_1.printSuccess)('arca', 'PDF created successfully');
223
+ return pdfRes.file;
224
+ }
225
+ catch (err) {
226
+ (0, helpers_1.printDanger)('arca', 'Error creating PDF');
227
+ console.log(JSON.stringify(err, null, 2));
228
+ helpers_1.AppError.Throw({ statusCodeName: 'INTERNAL_SERVER_ERROR', errorCode: 'PDF_CREATION_FAILED' });
229
+ }
230
+ }
223
231
  }
224
232
  exports.Arca = Arca;
233
+ // internal functions
234
+ function getReceiverFields(receiver) {
235
+ const isFinalConsumer = receiver.documentType === '99_final_consumer';
236
+ return {
237
+ receiverDocumentNumber: isFinalConsumer ? 0 : receiver.documentNumber || 0,
238
+ receiverVatCondition: receiver.vatCondition || '5_final_consumer',
239
+ };
240
+ }
241
+ function getLocalDate() {
242
+ return new Date(Date.now() + -3 * 60 * 60000);
243
+ }
244
+ function getArcaDate(date) {
245
+ return Number(date.replace(/-/g, ''));
246
+ }
247
+ function formatDateSlash(date) {
248
+ return date.split('-').reverse().join('/');
249
+ }
@@ -5,10 +5,12 @@ type GeneralUploadParamsConfig = {
5
5
  export declare abstract class FileStorage {
6
6
  static GenerateDownloadUrl(fileName: string): Promise<string>;
7
7
  static GenerateDownloadUrls(fileNames: string[]): Promise<string[]>;
8
- static UploadImage(image: UploadableImage, folder: string, previousFile?: string | null): Promise<string>;
8
+ static UploadImage(image: UploadableImage, folder: string, previousImage?: string | null): Promise<string>;
9
9
  static UploadImages(images: UploadableImage[], folder: string): Promise<string[]>;
10
- static UploadVideo(file: MulterFile, folder: string): Promise<string>;
11
- static UploadVideos(files: MulterFile[], folder: string): Promise<string[]>;
10
+ static UploadVideo(video: MulterFile, folder: string, previousVideo?: string | null): Promise<string>;
11
+ static UploadVideos(videos: MulterFile[], folder: string): Promise<string[]>;
12
+ static UploadFile(file: UploadableFile, folder: string, previousFile?: string | null): Promise<string>;
13
+ static UploadFiles(files: UploadableFile[], folder: string): Promise<string[]>;
12
14
  static GenerateUploadUrl(config: GeneralUploadParamsConfig): Promise<{
13
15
  url: string;
14
16
  fileName: string;
@@ -27,18 +27,38 @@ class FileStorage {
27
27
  static GenerateDownloadUrls(fileNames) {
28
28
  return Promise.all(fileNames.map((fileName) => this.GenerateDownloadUrl(fileName)));
29
29
  }
30
- static async UploadImage(image, folder, previousFile) {
31
- if (previousFile)
32
- await this.DeleteFile(previousFile);
30
+ static async UploadImage(image, folder, previousImage) {
31
+ if (previousImage)
32
+ await this.DeleteFile(previousImage);
33
33
  const isString = typeof image === 'string';
34
34
  const buffer = isString ? await promises_1.default.readFile(path_1.default.join(process.cwd(), image)) : image.buffer;
35
35
  const fileType = isString ? 'image/png' : image.mimetype;
36
36
  return uploadFile(buffer, { folder, fileType });
37
37
  }
38
- static UploadImages(images, folder) {
38
+ static async UploadImages(images, folder) {
39
39
  return Promise.all(images.map((image) => this.UploadImage(image, folder)));
40
40
  }
41
- static async UploadVideo(file, folder) {
41
+ static async UploadVideo(video, folder, previousVideo) {
42
+ if (previousVideo)
43
+ await this.DeleteFile(previousVideo);
44
+ const stream = fs_1.default.createReadStream(video.path);
45
+ try {
46
+ return await uploadFile(stream, { folder, fileType: video.mimetype });
47
+ }
48
+ finally {
49
+ promises_1.default.unlink(video.path);
50
+ }
51
+ }
52
+ static UploadVideos(videos, folder) {
53
+ return Promise.all(videos.map((video) => this.UploadVideo(video, folder)));
54
+ }
55
+ static async UploadFile(file, folder, previousFile) {
56
+ if (previousFile)
57
+ await this.DeleteFile(previousFile);
58
+ if (typeof file === 'string') {
59
+ const buffer = await promises_1.default.readFile(path_1.default.join(process.cwd(), file));
60
+ return uploadFile(buffer, { folder, fileType: 'application/octet-stream' });
61
+ }
42
62
  const stream = fs_1.default.createReadStream(file.path);
43
63
  try {
44
64
  return await uploadFile(stream, { folder, fileType: file.mimetype });
@@ -47,8 +67,8 @@ class FileStorage {
47
67
  promises_1.default.unlink(file.path);
48
68
  }
49
69
  }
50
- static UploadVideos(files, folder) {
51
- return Promise.all(files.map((file) => this.UploadVideo(file, folder)));
70
+ static UploadFiles(files, folder) {
71
+ return Promise.all(files.map((file) => this.UploadFile(file, folder)));
52
72
  }
53
73
  static async GenerateUploadUrl(config) {
54
74
  const params = { ...getGeneralUploadParams(config), Expires: expiresTime };
@@ -2,3 +2,5 @@ export declare const processImage: (req: Req, res: Res, next: Next) => void;
2
2
  export declare const processImages: (req: Req, res: Res, next: Next) => void;
3
3
  export declare const processVideo: (req: Req, res: Res, next: Next) => void;
4
4
  export declare const processVideos: (req: Req, res: Res, next: Next) => void;
5
+ export declare const processFile: (req: Req, res: Res, next: Next) => void;
6
+ export declare const processFiles: (req: Req, res: Res, next: Next) => void;
@@ -3,23 +3,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.processVideos = exports.processVideo = exports.processImages = exports.processImage = void 0;
6
+ exports.processFiles = exports.processFile = exports.processVideos = exports.processVideo = exports.processImages = exports.processImage = void 0;
7
7
  const multer_1 = __importDefault(require("multer"));
8
8
  // helpers
9
9
  const helpers_1 = require("../helpers");
10
10
  const imageStorage = multer_1.default.memoryStorage();
11
11
  const videoStorage = multer_1.default.diskStorage({});
12
+ const fileStorage = multer_1.default.diskStorage({});
12
13
  const invalidInputDataError = new helpers_1.AppError({ statusCodeName: 'BAD_REQUEST', errorCode: 'INVALID_INPUT_DATA' });
13
14
  exports.processImage = getProcessFn({ type: 'image', method: 'single' });
14
15
  exports.processImages = getProcessFn({ type: 'image', method: 'array' });
15
16
  exports.processVideo = getProcessFn({ type: 'video', method: 'single' });
16
17
  exports.processVideos = getProcessFn({ type: 'video', method: 'array' });
18
+ exports.processFile = getProcessFn({ type: 'file', method: 'single' });
19
+ exports.processFiles = getProcessFn({ type: 'file', method: 'array' });
17
20
  function getProcessFn({ type, method }) {
18
21
  return (req, res, next) => {
19
22
  const process = (0, multer_1.default)({
20
- storage: type === 'image' ? imageStorage : videoStorage,
23
+ storage: type === 'image' ? imageStorage : type === 'video' ? videoStorage : fileStorage,
21
24
  fileFilter: (_req, file, next) => {
22
- if (!file.mimetype.startsWith(`${type}/`)) {
25
+ if (type !== 'file' && !file.mimetype.startsWith(`${type}/`)) {
23
26
  next(invalidInputDataError);
24
27
  return;
25
28
  }
@@ -1,9 +1,9 @@
1
1
  export declare class Whatsapp {
2
2
  private __accessToken;
3
- private __phoneNumberId;
3
+ private __phoneNumber;
4
4
  constructor(config: {
5
5
  accessToken: string;
6
- phoneNumberId: string;
6
+ phoneNumber: string;
7
7
  });
8
8
  sendText(to: string, text: string): Promise<import("@whatsapp-cloudapi/types/cloudapi", { with: { "resolution-mode": "import" } }).CloudAPIResponse>;
9
9
  }
@@ -8,10 +8,10 @@ const client_1 = __importDefault(require("@whatsapp-cloudapi/client"));
8
8
  class Whatsapp {
9
9
  constructor(config) {
10
10
  this.__accessToken = config.accessToken;
11
- this.__phoneNumberId = config.phoneNumberId;
11
+ this.__phoneNumber = config.phoneNumber;
12
12
  }
13
- async sendText(to, text) {
14
- return client_1.default.sendTextMessage({ accessToken: this.__accessToken, from: this.__phoneNumberId, to, text });
13
+ sendText(to, text) {
14
+ return client_1.default.sendTextMessage({ accessToken: this.__accessToken, from: this.__phoneNumber, to, text });
15
15
  }
16
16
  }
17
17
  exports.Whatsapp = Whatsapp;
@@ -60,10 +60,12 @@ async function setFilesUrls(data) {
60
60
  const lowerKey = key.toLowerCase();
61
61
  if (lowerKey.endsWith('url') || lowerKey.endsWith('urls'))
62
62
  return;
63
- if ((lowerKey.endsWith('image') || lowerKey.endsWith('video')) && (typeof val === 'string' || val === null)) {
63
+ if ((lowerKey.endsWith('image') || lowerKey.endsWith('video') || key === 'file' || key.endsWith('File')) &&
64
+ (typeof val === 'string' || val === null)) {
64
65
  return 'single';
65
66
  }
66
- if ((lowerKey.endsWith('images') || lowerKey.endsWith('videos')) && Array.isArray(val))
67
+ if ((lowerKey.endsWith('images') || lowerKey.endsWith('videos') || key === 'files' || key.endsWith('Files')) &&
68
+ Array.isArray(val))
67
69
  return 'plural';
68
70
  }
69
71
  function collect(node) {
@@ -62,7 +62,6 @@ export type ArcaCreateInvoiceCOptions = {
62
62
  email?: string | null;
63
63
  };
64
64
  concept: ArcaConcept;
65
- items: ArcaCreateInvoiceCOptionsItem[];
66
65
  amount: number;
67
66
  service?: {
68
67
  from: string;
@@ -72,7 +71,10 @@ export type ArcaCreateInvoiceCOptions = {
72
71
  };
73
72
  export type ArcaCreateVoucherRes = {
74
73
  voucherNumber: number;
74
+ issueDate: string;
75
75
  cae: string;
76
76
  caeExpirationDate: string;
77
- file?: string;
77
+ };
78
+ export type ArcaCreateInvoiceCPdfOptions = ArcaCreateInvoiceCOptions & ArcaCreateVoucherRes & {
79
+ items: ArcaCreateInvoiceCOptionsItem[];
78
80
  };
@@ -22,4 +22,5 @@ declare global {
22
22
  type TimeStr = string;
23
23
  type MulterFile = Express.Multer.File;
24
24
  type UploadableImage = string | MulterFile;
25
+ type UploadableFile = string | MulterFile;
25
26
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pangea-server",
3
3
  "description": "",
4
- "version": "3.3.144",
4
+ "version": "3.3.145",
5
5
  "engines": {
6
6
  "node": "22.14.0"
7
7
  },