diff --git a/src/composables/useNotify.js b/src/composables/useNotify.js
new file mode 100644
index 000000000..30bb70b28
--- /dev/null
+++ b/src/composables/useNotify.js
@@ -0,0 +1,15 @@
+import { Notify } from 'quasar';
+import { i18n } from 'src/boot/i18n';
+
+export default function useNotify() {
+ const notify = (message, type) => {
+ Notify.create({
+ message: i18n.global.t(message),
+ type: type,
+ });
+ };
+
+ return {
+ notify,
+ };
+}
diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js
index 5fec1bdc2..106bc1a6a 100644
--- a/src/i18n/en/index.js
+++ b/src/i18n/en/index.js
@@ -390,6 +390,18 @@ export default {
shipped: 'Shipped',
totalWithVat: 'Amount',
},
+ globalInvoices: {
+ errors: {
+ chooseValidClient: 'Choose a valid client',
+ chooseValidCompany: 'Choose a valid company',
+ chooseValidPrinter: 'Choose a valid printer',
+ fillDates: 'Invoice date and the max date should be filled',
+ invoiceDateLessThanMaxDate: "Invoice date can't be less than max date",
+ invoiceWithFutureDate: 'Exists an invoice with a future date',
+ noTicketsToInvoice: "There aren't clients to invoice",
+ criticalInvoiceError: 'Critical invoicing error, process stopped',
+ },
+ },
},
worker: {
pageTitles: {
diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js
index 671dfa3a4..c15d6e714 100644
--- a/src/i18n/es/index.js
+++ b/src/i18n/es/index.js
@@ -390,6 +390,20 @@ export default {
shipped: 'F. envío',
totalWithVat: 'Importe',
},
+ globalInvoices: {
+ errors: {
+ chooseValidClient: 'Selecciona un cliente válido',
+ chooseValidCompany: 'Selecciona una empresa válida',
+ chooseValidPrinter: 'Selecciona una impresora válida',
+ fillDates:
+ 'La fecha de la factura y la fecha máxima deben estar completas',
+ invoiceDateLessThanMaxDate:
+ 'La fecha de la factura no puede ser menor que la fecha máxima',
+ invoiceWithFutureDate: 'Existe una factura con una fecha futura',
+ noTicketsToInvoice: 'No hay clientes para facturar',
+ criticalInvoiceError: 'Error crítico en la facturación, proceso detenido',
+ },
+ },
},
worker: {
pageTitles: {
diff --git a/src/pages/InvoiceOut/InvoiceOutGlobal.vue b/src/pages/InvoiceOut/InvoiceOutGlobal.vue
index 6fb6e618a..2b260ae9e 100644
--- a/src/pages/InvoiceOut/InvoiceOutGlobal.vue
+++ b/src/pages/InvoiceOut/InvoiceOutGlobal.vue
@@ -1,17 +1,95 @@
@@ -20,13 +98,96 @@ onUnmounted(() => (stateStore.rightDrawer = false));
- Aca iria una tabla
+
+
+
+
+ {{ cardStatusText }}
+ {{ percentageStatusText }}
+ {{ pdfStatusText }}
+
+
+
+
+
+
+
+ {{ props.value }}
+
+
+
+
+
+
+
+
-
+
+
+
+ {
+ "en": {
+ "status": {
+ "packageInvoicing": "Build packaging tickets",
+ "invoicing": "Invoicing client",
+ "stopping": "Stopping process",
+ "done": "Ended process"
+ },
+ "of": "of"
+ },
+ "es": {
+ "status":{
+ "packageInvoicing": "Generación de tickets de empaque",
+ "invoicing": "Facturando a cliente",
+ "stopping": "Deteniendo proceso",
+ "done": "Proceso detenido",
+ },
+ "of": "de"
+ }
+ }
+
diff --git a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue
index 6d29d9ba5..baa1b86ed 100644
--- a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue
+++ b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue
@@ -1,100 +1,60 @@
-
+
{};
{};
rounded
dense
/>
+
@@ -197,7 +170,8 @@ const makeInvoice = () => {};
"company": "Company",
"printer": "Printer",
"invoiceOut": "Invoice out",
- "client": "Client"
+ "client": "Client",
+ "stop": "Stop"
},
"es": {
"invoiceDate": "Fecha de factura",
@@ -207,8 +181,8 @@ const makeInvoice = () => {};
"company": "Empresa",
"printer": "Impresora",
"invoiceOut": "Facturar",
- "client": "Cliente"
-
+ "client": "Cliente",
+ "stop": "Parar"
}
}
diff --git a/src/services/InvoiceOut.service.js b/src/services/InvoiceOut.service.js
index b5f23869e..039943483 100644
--- a/src/services/InvoiceOut.service.js
+++ b/src/services/InvoiceOut.service.js
@@ -1,52 +1,52 @@
import axios from 'axios';
-const invoiceOutService = {
- getInvoiceOutConfig: async (path = '', params) => {
- try {
- const { data } = await axios.get(path, {
- params,
- });
+const request = async (method, url, params = {}) => {
+ try {
+ let response;
- return data;
- } catch (err) {
- console.error('Error fetching invoice date');
+ if (method === 'GET') {
+ response = await axios.get(url, { params });
+ } else if (method === 'POST') {
+ response = await axios.post(url, params);
}
+ return response.data;
+ } catch (err) {
+ console.error(`Error with ${method} request to ${url}`, err);
+ return err.response;
+ }
+};
+
+const invoiceOutService = {
+ getInvoiceDate: async (params) => {
+ return await request('GET', 'InvoiceOuts/getInvoiceDate', params);
+ },
+
+ getFindOne: async (params) => {
+ return await request('GET', 'InvoiceOutConfigs/findOne', params);
},
getCompanies: async (filter) => {
- try {
- const { data } = await axios.get('Companies', {
- filter,
- });
-
- return data;
- } catch (err) {
- console.error('Error fetching companies');
- }
+ return await request('GET', 'Companies', { filter });
},
getPrinters: async (filter) => {
- try {
- const { data } = await axios.get('Printers', {
- filter,
- });
-
- return data;
- } catch (err) {
- console.error('Error fetching printers');
- }
+ return await request('GET', 'Printers', { filter });
},
getClients: async (filter) => {
- try {
- const { data } = await axios.get('Clients', {
- filter,
- });
+ return await request('GET', 'Clients', { filter });
+ },
- return data;
- } catch (err) {
- console.error('Error fetching clients');
- }
+ getClientsToInvoice: async (params) => {
+ return await request('POST', 'InvoiceOuts/clientsToInvoice', params);
+ },
+
+ invoiceClient: async (params) => {
+ return await request('POST', 'InvoiceOuts/invoiceClient', params);
+ },
+
+ makePdfAndNotify: async (invoiceId, params) => {
+ return await request('POST', `InvoiceOuts/${invoiceId}/makePdfAndNotify`, params);
},
};
diff --git a/src/stores/invoiceOutGlobal.js b/src/stores/invoiceOutGlobal.js
new file mode 100644
index 000000000..7223f7655
--- /dev/null
+++ b/src/stores/invoiceOutGlobal.js
@@ -0,0 +1,286 @@
+import { defineStore } from 'pinia';
+import { useUserConfig } from 'src/composables/useUserConfig';
+import invoiceOutService from 'src/services/InvoiceOut.service.js';
+import useNotify from 'src/composables/useNotify.js';
+
+const { notify } = useNotify();
+
+export const useInvoiceOutGlobalStore = defineStore({
+ id: 'invoiceOutGlobal',
+
+ state: () => ({
+ initialDataLoading: true,
+ formInitialData: {
+ companyFk: null,
+ invoiceDate: null,
+ maxShipped: null,
+ clientId: null,
+ },
+ clientsOptions: [],
+ companiesOptions: [],
+ printersOptions: [],
+ addresses: [],
+ minInvoicingDate: null,
+ parallelism: null,
+ invoicing: false,
+ isInvoicing: false,
+ status: null,
+ addressIndex: 0,
+ printer: null,
+ errors: [],
+ nRequests: 0,
+ nPdfs: 0,
+ totalPdfs: 0,
+ }),
+ actions: {
+ async init() {
+ await this.fetchAllData();
+ },
+
+ async fetchAllData() {
+ try {
+ const userInfo = await useUserConfig().fetch();
+ const date = Date.vnNew();
+ this.formInitialData.maxShipped = new Date(
+ date.getFullYear(),
+ date.getMonth(),
+ 0
+ )
+ .toISOString()
+ .substring(0, 10);
+
+ await Promise.all([
+ this.fetchClients(),
+ this.fetchParallelism(),
+ this.fetchCompanies(userInfo.companyFk),
+ this.fetchPrinters(),
+ this.fetchInvoiceOutConfig(userInfo.companyFk),
+ ]);
+
+ this.initialDataLoading = false;
+ } catch (err) {
+ console.error('Error fetching invoice out global initial data');
+ }
+ },
+
+ async fetchClients() {
+ const clientsFilter = { fields: ['id', 'name'], order: 'id', limit: 30 };
+ const clientsResponse = await invoiceOutService.getClients(clientsFilter);
+ this.clientsOptions = clientsResponse.map((client) => {
+ return { value: client.id, label: client.name };
+ });
+ },
+
+ async fetchCompanies(companyFk) {
+ const companiesFilters = { order: ['code'] };
+ const companiesResponse = await invoiceOutService.getCompanies(
+ companiesFilters
+ );
+ this.companiesOptions = companiesResponse.map((company) => {
+ return { value: company.id, label: company.code };
+ });
+
+ this.formInitialData.companyFk = this.companiesOptions.find(
+ (company) => companyFk === company.value
+ );
+ },
+
+ async fetchPrinters() {
+ const printersFilters = {
+ fields: ['id', 'name'],
+ where: { isLabeler: false },
+ order: 'name ASC',
+ limit: 30,
+ };
+ const printersResponse = await invoiceOutService.getPrinters(printersFilters);
+ this.printersOptions = printersResponse.map((printer) => {
+ return { value: printer.id, label: printer.name };
+ });
+ },
+
+ async fetchInvoiceOutConfig(companyFk) {
+ const params = { companyFk: companyFk };
+ const { issued } = await invoiceOutService.getInvoiceDate(params);
+
+ const stringDate = issued.substring(0, 10);
+ this.minInvoicingDate = stringDate;
+ this.formInitialData.invoiceDate = stringDate;
+ },
+
+ async fetchParallelism() {
+ const filter = { fields: ['parallelism'] };
+ const { parallelism } = await invoiceOutService.getFindOne(filter);
+ this.parallelism = parallelism;
+ },
+
+ async makeInvoice(formData, clientsToInvoice) {
+ this.invoicing = true;
+ this.status = 'packageInvoicing';
+ try {
+ const params = {
+ invoiceDate: new Date(formData.invoiceDate),
+ maxShipped: new Date(formData.maxShipped),
+ clientId: formData.clientId ? formData.clientId.value : null,
+ companyFk: formData.companyFk.value,
+ };
+
+ this.validateMakeInvoceParams(params, clientsToInvoice);
+
+ if (clientsToInvoice == 'all') params.clientId = undefined;
+
+ const addressesResponse = await invoiceOutService.getClientsToInvoice(
+ params
+ );
+
+ this.addresses = addressesResponse;
+
+ if (!this.addresses || !this.addresses.length > 0) {
+ notify(
+ 'invoiceOut.globalInvoices.errors.noTicketsToInvoice',
+ 'negative'
+ );
+ throw new Error("There aren't addresses to invoice");
+ }
+
+ this.addresses.forEach(async (address) => {
+ await this.invoiceClient(address, formData);
+ });
+ } catch (err) {
+ this.handleError(err);
+ }
+ },
+
+ validateMakeInvoceParams(params, clientsToInvoice) {
+ if (clientsToInvoice === 'one' && !params.clientId) {
+ notify('invoiceOut.globalInvoices.errors.chooseValidClient', 'negative');
+ throw new Error('Invalid client');
+ }
+ if (!params.invoiceDate || !params.maxShipped) {
+ notify('invoiceOut.globalInvoices.errors.fillDates', 'negative');
+ throw new Error('Missing dates');
+ }
+ if (params.invoiceDate < params.maxShipped) {
+ notify(
+ 'invoiceOut.globalInvoices.errors.invoiceDateLessThanMaxDate',
+ 'negative'
+ );
+ throw new Error('Invalid date range');
+ }
+
+ const invoiceDateTime = new Date(params.invoiceDate).getTime();
+ const minInvoiceDateTime = new Date(this.minInvoicingDate).getTime();
+
+ if (this.minInvoicingDate && invoiceDateTime < minInvoiceDateTime) {
+ notify(
+ 'invoiceOut.globalInvoices.errors.invoiceWithFutureDate',
+ 'negative'
+ );
+ throw new Error('Invoice date in the future');
+ }
+
+ if (!params.companyFk) {
+ notify('invoiceOut.globalInvoices.errors.chooseValidCompany', 'negative');
+ throw new Error('Invalid company');
+ }
+ if (!this.printer) {
+ notify('invoiceOut.globalInvoices.errors.chooseValidPrinter', 'negative');
+ throw new Error('Invalid printer');
+ }
+ },
+
+ async invoiceClient(address, formData) {
+ if (this.nRequests === this.parallelism || this.isInvoicing) return;
+
+ if (this.status === 'stopping') {
+ if (this.nRequests) return;
+ this.invoicing = false;
+ this.status = 'done';
+ return;
+ }
+
+ const params = {
+ clientId: address.clientId,
+ addressId: address.id,
+ invoiceDate: new Date(formData.invoiceDate),
+ maxShipped: new Date(formData.maxShipped),
+ companyFk: formData.companyFk.value,
+ };
+
+ this.status = 'invoicing';
+ this.invoicing = true;
+
+ const invoiceResponse = await invoiceOutService.invoiceClient(params);
+
+ if (invoiceResponse.data.error) {
+ if (invoiceResponse.status >= 400 && invoiceResponse.status < 500) {
+ this.invoiceClientError(address, invoiceResponse);
+ this.addressIndex++;
+ return;
+ } else {
+ this.invoicing = false;
+ this.status = 'done';
+ notify(
+ 'invoiceOut.globalInvoices.errors.criticalInvoiceError',
+ 'negative'
+ );
+ throw new Error('Critical invoicing error, process stopped');
+ }
+ } else {
+ this.isInvoicing = false;
+ if (invoiceResponse.data) {
+ this.makePdfAndNotify(invoiceResponse.data, address);
+ }
+ }
+ },
+
+ async makePdfAndNotify(invoiceId, client) {
+ try {
+ this.nRequests++;
+ this.totalPdfs++;
+ const params = { printerFk: this.printer.value };
+ await invoiceOutService.makePdfAndNotify(invoiceId, params);
+ this.nPdfs++;
+ this.nRequests--;
+ } catch (err) {
+ this.invoiceClientError(client, err, true);
+ }
+ },
+
+ invoiceClientError(client, response, isWarning) {
+ const message = response.data?.error?.message || response.message;
+ this.errors.unshift({ client, message, isWarning });
+ },
+
+ handleError(err) {
+ this.invoicing = false;
+ this.status = null;
+ throw err;
+ },
+
+ // State mutations actions
+
+ setPrinterValue(printer) {
+ this.printer = printer;
+ },
+
+ setStatusValue(status) {
+ this.status = status;
+ },
+ },
+
+ getters: {
+ getNAddresses(state) {
+ return state.addresses.length;
+ },
+ getPercentage(state) {
+ if (this.getNAdresses <= 0 || !state.addressIndex) {
+ return 0;
+ }
+ let porcentaje = (state.addressIndex / this.getNAddresses) * 100;
+ return porcentaje;
+ },
+ getAddressNumber(state) {
+ return state.addressIndex;
+ },
+ },
+});