diff --git a/db/changes/10340-summer/00-sample.sql b/db/changes/10340-summer/00-sample.sql new file mode 100644 index 000000000..d4858ac72 --- /dev/null +++ b/db/changes/10340-summer/00-sample.sql @@ -0,0 +1,7 @@ +ALTER TABLE `vn`.`sample` ADD COLUMN + (`datepickerEnabled` TINYINT(1) NOT NULL DEFAULT 0); + +ALTER TABLE `vn`.`sample` MODIFY code VARCHAR(25) charset utf8 NOT NULL; + +INSERT INTO `vn`.`sample` (code, description, isVisible, hasCompany, hasPreview, datepickerEnabled) + VALUES ('client-debt-statement', 'Extracto del cliente', 1, 0, 1, 1); \ No newline at end of file diff --git a/modules/client/back/models/sample.json b/modules/client/back/models/sample.json index 725bfb9c7..cfb127ab2 100644 --- a/modules/client/back/models/sample.json +++ b/modules/client/back/models/sample.json @@ -9,23 +9,26 @@ "properties": { "id": { "id": true, - "type": "Number", + "type": "number", "description": "Identifier" }, "code": { - "type": "String" + "type": "string" }, "description": { - "type": "String" + "type": "string" }, "isVisible": { - "type": "Boolean" + "type": "boolean" }, "hasCompany": { - "type": "Boolean" + "type": "boolean" }, "hasPreview": { - "type": "Boolean" + "type": "boolean" + }, + "datepickerEnabled": { + "type": "boolean" } }, "scopes": { diff --git a/modules/client/front/sample/create/index.html b/modules/client/front/sample/create/index.html index e6733a656..2d0f3d29c 100644 --- a/modules/client/front/sample/create/index.html +++ b/modules/client/front/sample/create/index.html @@ -25,38 +25,48 @@
- - - - - - - - + label="Sample" + required="true"> + + + + + + + + + ng-if="sampleType.selection.hasCompany" + required="true"> + + diff --git a/modules/client/front/sample/create/index.js b/modules/client/front/sample/create/index.js index 0eaeddc20..19fe1fecf 100644 --- a/modules/client/front/sample/create/index.js +++ b/modules/client/front/sample/create/index.js @@ -80,6 +80,12 @@ class Controller extends Section { if (sampleType.hasCompany) params.companyId = this.clientSample.companyFk; + if (sampleType.datepickerEnabled && !this.clientSample.from) + return this.vnApp.showError(this.$t('Choose a date')); + + if (sampleType.datepickerEnabled) + params.from = this.clientSample.from; + let query = `email/${sampleType.code}`; if (isPreview) query = `email/${sampleType.code}/preview`; diff --git a/modules/client/front/sample/create/locale/es.yml b/modules/client/front/sample/create/locale/es.yml index a534e05d4..b72d456d8 100644 --- a/modules/client/front/sample/create/locale/es.yml +++ b/modules/client/front/sample/create/locale/es.yml @@ -1,5 +1,6 @@ Choose a sample: Selecciona una plantilla Choose a company: Selecciona una empresa +Choose a date: Selecciona una fecha Email cannot be blank: Debes introducir un email Recipient: Destinatario Its only used when sample is sent: Se utiliza únicamente cuando se envía la plantilla diff --git a/print/core/mixins/db-helper.js b/print/core/mixins/db-helper.js index 791766775..38eb50a1d 100644 --- a/print/core/mixins/db-helper.js +++ b/print/core/mixins/db-helper.js @@ -89,8 +89,7 @@ const dbHelper = { const absolutePath = path.join(__dirname, '../', this.path, 'sql', queryName); return db.getSqlFromDef(absolutePath); }, - }, - props: ['tplPath'] + } }; Vue.mixin(dbHelper); diff --git a/print/templates/email/client-debt-statement/assets/css/import.js b/print/templates/email/client-debt-statement/assets/css/import.js new file mode 100644 index 000000000..b44d6bd37 --- /dev/null +++ b/print/templates/email/client-debt-statement/assets/css/import.js @@ -0,0 +1,8 @@ +const Stylesheet = require(`${appPath}/core/stylesheet`); + +module.exports = new Stylesheet([ + `${appPath}/common/css/spacing.css`, + `${appPath}/common/css/misc.css`, + `${appPath}/common/css/layout.css`, + `${appPath}/common/css/email.css`]) + .mergeStyles(); diff --git a/print/templates/email/client-debt-statement/attachments.json b/print/templates/email/client-debt-statement/attachments.json new file mode 100644 index 000000000..9cc4911e6 --- /dev/null +++ b/print/templates/email/client-debt-statement/attachments.json @@ -0,0 +1,6 @@ +[ + { + "filename": "client-debt-statement.pdf", + "component": "client-debt-statement" + } +] \ No newline at end of file diff --git a/print/templates/email/client-debt-statement/client-debt-statement.html b/print/templates/email/client-debt-statement/client-debt-statement.html new file mode 100644 index 000000000..e63eba7f7 --- /dev/null +++ b/print/templates/email/client-debt-statement/client-debt-statement.html @@ -0,0 +1,55 @@ + + + + + + {{ $t('subject') }} + + + + + + + + +
+ +
+
+
+ +
+
+ +
+
+ +
+
+

{{ $t('title') }}

+

{{$t('description.instructions')}}

+
+
+ +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/print/templates/email/client-debt-statement/client-debt-statement.js b/print/templates/email/client-debt-statement/client-debt-statement.js new file mode 100755 index 000000000..c32e68943 --- /dev/null +++ b/print/templates/email/client-debt-statement/client-debt-statement.js @@ -0,0 +1,25 @@ +const Component = require(`${appPath}/core/component`); +const emailHeader = new Component('email-header'); +const emailFooter = new Component('email-footer'); +const attachment = new Component('attachment'); +const attachments = require('./attachments.json'); + +module.exports = { + name: 'client-debt-statement', + components: { + 'email-header': emailHeader.build(), + 'email-footer': emailFooter.build(), + 'attachment': attachment.build() + }, + data() { + return {attachments}; + }, + props: { + recipientId: { + required: true + }, + from: { + required: true + } + } +}; diff --git a/print/templates/email/client-debt-statement/locale/es.yml b/print/templates/email/client-debt-statement/locale/es.yml new file mode 100644 index 000000000..754e8e92c --- /dev/null +++ b/print/templates/email/client-debt-statement/locale/es.yml @@ -0,0 +1,4 @@ +subject: Extracto de tu balance +title: Extracto de tu balance +description: + instructions: Adjuntamos el extracto de tu balance. \ No newline at end of file diff --git a/print/templates/reports/client-debt-statement/assets/css/import.js b/print/templates/reports/client-debt-statement/assets/css/import.js new file mode 100644 index 000000000..fd8796c2b --- /dev/null +++ b/print/templates/reports/client-debt-statement/assets/css/import.js @@ -0,0 +1,9 @@ +const Stylesheet = require(`${appPath}/core/stylesheet`); + +module.exports = new Stylesheet([ + `${appPath}/common/css/spacing.css`, + `${appPath}/common/css/misc.css`, + `${appPath}/common/css/layout.css`, + `${appPath}/common/css/report.css`, + `${__dirname}/style.css`]) + .mergeStyles(); diff --git a/print/templates/reports/client-debt-statement/assets/css/style.css b/print/templates/reports/client-debt-statement/assets/css/style.css new file mode 100644 index 000000000..e621f3e23 --- /dev/null +++ b/print/templates/reports/client-debt-statement/assets/css/style.css @@ -0,0 +1,3 @@ +table.column-oriented { + margin-top: 50px !important +} \ No newline at end of file diff --git a/print/templates/reports/client-debt-statement/client-debt-statement.html b/print/templates/reports/client-debt-statement/client-debt-statement.html new file mode 100644 index 000000000..88bf15bdb --- /dev/null +++ b/print/templates/reports/client-debt-statement/client-debt-statement.html @@ -0,0 +1,95 @@ + + + + + + + + + +
+ + + +
+
+
+
+
+

{{$t('title')}}

+ + + + + + + + + + + +
{{$t('clientId')}}{{client.id}}
{{$t('date')}}{{dated}}
+
+
+
+
+
{{$t('clientData')}}
+
+

{{client.socialName}}

+
+ {{client.street}} +
+
+ {{client.postcode}}, {{client.city}} ({{client.province}}) +
+
+ {{client.country}} +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{$t('date')}}{{$t('concept')}}{{$t('invoiced')}}{{$t('payed')}}{{$t('balance')}}
{{sale.issued | date('%d-%m-%Y')}}{{sale.ref}}{{sale.debtOut}}{{sale.debtIn}}{{getBalance(sale)}}
+ Total + {{getTotalDebtOut() | currency('EUR', $i18n.locale)}} + {{getTotalDebtIn() | currency('EUR', $i18n.locale)}}{{totalBalance | currency('EUR', $i18n.locale)}}
+
+
+ + + +
+ + \ No newline at end of file diff --git a/print/templates/reports/client-debt-statement/client-debt-statement.js b/print/templates/reports/client-debt-statement/client-debt-statement.js new file mode 100755 index 000000000..09b99590b --- /dev/null +++ b/print/templates/reports/client-debt-statement/client-debt-statement.js @@ -0,0 +1,78 @@ +const Component = require(`${appPath}/core/component`); +const reportHeader = new Component('report-header'); +const reportFooter = new Component('report-footer'); + +module.exports = { + name: 'client-debt-statement', + async serverPrefetch() { + this.client = await this.fetchClient(this.recipientId); + this.sales = await this.fetchSales(this.recipientId, this.from); + + if (!this.client) + throw new Error('Something went wrong'); + }, + computed: { + dated: function() { + const filters = this.$options.filters; + + return filters.date(new Date(), '%d-%m-%Y'); + } + }, + data() { + return {totalBalance: 0.00}; + }, + methods: { + fetchClient(clientId) { + return this.findOneFromDef('client', [clientId]); + }, + fetchSales(clientId, from) { + return this.rawSqlFromDef('sales', [ + from, + clientId, + from, + clientId, + from, + clientId, + from, + clientId, + from, + clientId + ]); + }, + getBalance(sale) { + if (sale.debtOut) + this.totalBalance += parseFloat(sale.debtOut); + + if (sale.debtIn) + this.totalBalance -= parseFloat(sale.debtIn); + + return parseFloat(this.totalBalance.toFixed(2)); + }, + getTotalDebtOut() { + let debtOut = 0.00; + for (let sale of this.sales) + debtOut += sale.debtOut ? parseFloat(sale.debtOut) : 0; + + return debtOut.toFixed(2); + }, + getTotalDebtIn() { + let debtIn = 0.00; + for (let sale of this.sales) + debtIn += sale.debtIn ? parseFloat(sale.debtIn) : 0; + + return debtIn.toFixed(2); + }, + }, + components: { + 'report-header': reportHeader.build(), + 'report-footer': reportFooter.build() + }, + props: { + recipientId: { + required: true + }, + from: { + required: true + } + } +}; diff --git a/print/templates/reports/client-debt-statement/locale/es.yml b/print/templates/reports/client-debt-statement/locale/es.yml new file mode 100644 index 000000000..ccdce7b5b --- /dev/null +++ b/print/templates/reports/client-debt-statement/locale/es.yml @@ -0,0 +1,9 @@ +title: Extracto +clientId: Cliente +clientData: Datos del cliente +date: Fecha +concept: Concepto +invoiced: Facturado +payed: Pagado +balance: Saldo +client: Cliente {0} \ No newline at end of file diff --git a/print/templates/reports/client-debt-statement/locale/fr.yml b/print/templates/reports/client-debt-statement/locale/fr.yml new file mode 100644 index 000000000..12534f9ff --- /dev/null +++ b/print/templates/reports/client-debt-statement/locale/fr.yml @@ -0,0 +1,9 @@ +title: Relevé de compte +clientId: Client +clientData: Données client +date: Date +concept: Objet +invoiced: Facturé +payed: Payé +balance: Solde +client: Client {0} \ No newline at end of file diff --git a/print/templates/reports/client-debt-statement/sql/client.sql b/print/templates/reports/client-debt-statement/sql/client.sql new file mode 100644 index 000000000..d675cf168 --- /dev/null +++ b/print/templates/reports/client-debt-statement/sql/client.sql @@ -0,0 +1,13 @@ +SELECT + c.id, + c.socialName, + c.street, + c.postcode, + c.city, + c.fi, + p.name AS province, + ct.country +FROM client c + JOIN country ct ON ct.id = c.countryFk + LEFT JOIN province p ON p.id = c.provinceFk +WHERE c.id = ? \ No newline at end of file diff --git a/print/templates/reports/client-debt-statement/sql/sales.sql b/print/templates/reports/client-debt-statement/sql/sales.sql new file mode 100644 index 000000000..7a9124da5 --- /dev/null +++ b/print/templates/reports/client-debt-statement/sql/sales.sql @@ -0,0 +1,53 @@ +SELECT + issued, + CAST(debtOut AS DECIMAL(10,2)) debtOut, + CAST(debtIn AS DECIMAL(10,2)) debtIn, + ref, + companyFk, + priority +FROM ( + SELECT + ? AS issued, + SUM(amountUnpaid) AS debtOut, + NULL AS debtIn, + 'Saldo Anterior' AS ref, + companyFk, + 0 as priority + FROM ( + SELECT SUM(amount) AS amountUnpaid, companyFk, 0 + FROM invoiceOut io + WHERE io.clientFk = ? + AND io.issued < ? + GROUP BY io.companyFk + UNION ALL + SELECT SUM(-1 * amountPaid), companyFk, 0 + FROM receipt + WHERE clientFk = ? + AND payed < ? + GROUP BY companyFk) AS transactions + GROUP BY companyFk + UNION ALL + SELECT + issued, + amount as debtOut, + NULL AS debtIn, + ref, + companyFk, + 1 + FROM invoiceOut + WHERE clientFk = ? + AND issued >= ? + UNION ALL + SELECT + r.payed, + NULL as debtOut, + r.amountPaid, + r.invoiceFk, + r.companyFk, + 0 + FROM receipt r + WHERE r.clientFk = ? + AND r.payed >= ?) t + INNER JOIN `client` c ON c.id = ? +HAVING debtOut <> 0 OR debtIn <> 0 +ORDER BY issued, priority DESC, debtIn; \ No newline at end of file