From c3b9f013f6a17fc585d952cd25475986f448a4a0 Mon Sep 17 00:00:00 2001 From: wbuezas Date: Mon, 20 Nov 2023 14:58:41 -0300 Subject: [PATCH] form integration and view content --- src/composables/useNotify.js | 15 + src/i18n/en/index.js | 12 + src/i18n/es/index.js | 14 + src/pages/InvoiceOut/InvoiceOutGlobal.vue | 185 ++++++++++- src/pages/InvoiceOut/InvoiceOutGlobalForm.vue | 156 ++++------ src/services/InvoiceOut.service.js | 70 ++--- src/stores/invoiceOutGlobal.js | 286 ++++++++++++++++++ 7 files changed, 600 insertions(+), 138 deletions(-) create mode 100644 src/composables/useNotify.js create mode 100644 src/stores/invoiceOutGlobal.js 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 @@ - + + + + { + "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 @@ @@ -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; + }, + }, +});