Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5542-refund_refactor
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
c69e038a56
|
@ -8,7 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
## [2316.01] - 2023-05-04
|
||||
|
||||
### Added
|
||||
-
|
||||
- (Usuarios -> Histórico) Nueva sección
|
||||
- (Roles -> Histórico) Nueva sección
|
||||
|
||||
### Changed
|
||||
- (Artículo -> Precio fijado) Modificado el buscador superior por uno lateral
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
UPDATE `salix`.`ACL`
|
||||
SET model = 'InvoiceOut'
|
||||
WHERE property IN ('negativeBases', 'negativeBasesCsv');
|
|
@ -0,0 +1,5 @@
|
|||
INSERT INTO `vn`.`companyI18n` (`companyFk`, `lang`, `footnotes`)
|
||||
VALUES (442, 'en', 'In compliance with the provisions of Organic Law 15/1999, on the Protection of Personal Data, we inform you that the personal data you provide will be included in automated files of VERDNATURA LEVANTE SL, being able at all times to exercise the rights of access, rectification, cancellation and opposition, communicating it in writing to the registered office of the entity. The purpose of the file is administrative management, accounting, and billing.'),
|
||||
(442, 'es', 'En cumplimiento de lo dispuesto en la Ley Orgánica 15/1999, de Protección de Datos de Carácter Personal, le comunicamos que los datos personales que facilite se incluirán en ficheros automatizados de VERDNATURA LEVANTE S.L., pudiendo en todo momento ejercitar los derechos de acceso, rectificación, cancelación y oposición, comunicándolo por escrito al domicilio social de la entidad. La finalidad del fichero es la gestión administrativa, contabilidad, y facturación.'),
|
||||
(442, 'fr', 'Conformément aux dispositions de la loi organique 15/1999 sur la protection des données personnelles, nous vous informons que les données personnelles que vous fournissez seront incluses dans des dossiers. VERDNATURA LEVANTE S.L., vous pouvez à tout moment, exercer les droits d``accès, de rectification, d``annulation et d``opposition, en communiquant par écrit au siège social de la société. Le dossier a pour objet la gestion administrative, la comptabilité et la facturation.'),
|
||||
(442, 'pt', 'Em cumprimento do disposto na lei Orgânica 15/1999, de Protecção de Dados de Carácter Pessoal, comunicamos que os dados pessoais que facilite se incluirão nos ficheiros automatizados de VERDNATURA LEVANTE S.L., podendo em todo momento exercer os direitos de acesso, rectificação, cancelação e oposição, comunicando por escrito ao domicílio social da entidade. A finalidade do ficheiro é a gestão administrativa, contabilidade e facturação.');
|
|
@ -0,0 +1,9 @@
|
|||
-- vn.companyI18n definition
|
||||
CREATE TABLE `vn`.`companyI18n` (
|
||||
`companyFk` smallint(5) unsigned NOT NULL,
|
||||
`lang` char(2) CHARACTER SET utf8mb3 NOT NULL,
|
||||
`footnotes` longtext COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`companyFk`,`lang`),
|
||||
CONSTRAINT `companyI18n_FK` FOREIGN KEY (`companyFk`) REFERENCES `company` (`id`) ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE `vn`.`company` ADD `web` varchar(100) NULL;
|
|
@ -546,7 +546,8 @@ INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`
|
|||
VALUES
|
||||
(1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, util.VN_CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1, '400664487V'),
|
||||
(2, 'Farmer King', 'The farmer', 4000020002, 1, '87945234L', 0, util.VN_CURDATE(), 1, 'supplier address 2', 'GOTHAM', 2, 43022, 1, 2, 10, 93, 2, 8, 18, 'animals', 1, '400664487V'),
|
||||
(442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, util.VN_CURDATE(), 1, 'supplier address 3', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V');
|
||||
(442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, util.VN_CURDATE(), 1, 'supplier address 3', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'),
|
||||
(1381, 'Ornamentales', 'Ornamentales', 7185000440, 1, '03815934E', 0, util.VN_CURDATE(), 1, 'supplier address 4', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V');
|
||||
|
||||
INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`)
|
||||
VALUES
|
||||
|
@ -2789,7 +2790,7 @@ INSERT INTO `vn`.`profileType` (`id`, `name`)
|
|||
|
||||
INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
|
||||
VALUES
|
||||
('lilium', 'dev', 'http://localhost:8080/#/'),
|
||||
('lilium', 'dev', 'http://localhost:9000/#/'),
|
||||
('salix', 'dev', 'http://localhost:5000/#!/');
|
||||
|
||||
INSERT INTO `vn`.`report` (`id`, `name`, `paperSizeFk`, `method`)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('InvoiceIn negative bases path', () => {
|
||||
describe('InvoiceOut negative bases path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
const httpRequests = [];
|
||||
|
@ -9,11 +9,11 @@ describe('InvoiceIn negative bases path', () => {
|
|||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
page.on('request', req => {
|
||||
if (req.url().includes(`InvoiceIns/negativeBases`))
|
||||
if (req.url().includes(`InvoiceOuts/negativeBases`))
|
||||
httpRequests.push(req.url());
|
||||
});
|
||||
await page.loginAndModule('administrative', 'invoiceIn');
|
||||
await page.accessToSection('invoiceIn.negative-bases');
|
||||
await page.loginAndModule('administrative', 'invoiceOut');
|
||||
await page.accessToSection('invoiceOut.negative-bases');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
|
@ -156,6 +156,18 @@
|
|||
"Component cost not set": "Componente coste no está estabecido",
|
||||
"Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2",
|
||||
"Description cannot be blank": "Description cannot be blank",
|
||||
"company": "Company",
|
||||
"country": "Country",
|
||||
"clientId": "Id client",
|
||||
"clientSocialName": "Client",
|
||||
"amount": "Amount",
|
||||
"taxableBase": "Taxable base",
|
||||
"ticketFk": "Id ticket",
|
||||
"isActive": "Active",
|
||||
"hasToInvoice": "Invoice",
|
||||
"isTaxDataChecked": "Data checked",
|
||||
"comercialId": "Id Comercial",
|
||||
"comercialName": "Comercial",
|
||||
"Added observation": "Added observation",
|
||||
"Comment added to client": "Comment added to client"
|
||||
}
|
||||
|
|
|
@ -275,7 +275,19 @@
|
|||
"Collection does not exist": "La colección no existe",
|
||||
"Cannot obtain exclusive lock": "No se puede obtener un bloqueo exclusivo",
|
||||
"Insert a date range": "Inserte un rango de fechas",
|
||||
"Added observation": "{{user}} añadió esta observacion: {{text}}",
|
||||
"Comment added to client": "Observación añadida al cliente {{clientFk}}",
|
||||
"Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen"
|
||||
"Added observation": "{{user}} añadió esta observacion: {{text}}",
|
||||
"Comment added to client": "Observación añadida al cliente {{clientFk}}",
|
||||
"Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen",
|
||||
"company": "Compañía",
|
||||
"country": "País",
|
||||
"clientId": "Id cliente",
|
||||
"clientSocialName": "Cliente",
|
||||
"amount": "Importe",
|
||||
"taxableBase": "Base",
|
||||
"ticketFk": "Id ticket",
|
||||
"isActive": "Activo",
|
||||
"hasToInvoice": "Facturar",
|
||||
"isTaxDataChecked": "Datos comprobados",
|
||||
"comercialId": "Id comercial",
|
||||
"comercialName": "Comercial"
|
||||
}
|
||||
|
|
|
@ -1,48 +1 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
auto-load="true"
|
||||
filter="::$ctrl.filter"
|
||||
url="ClaimDms"
|
||||
link="{claimFk: $ctrl.$params.id}"
|
||||
limit="20"
|
||||
data="$ctrl.photos">
|
||||
</vn-crud-model>
|
||||
<vn-horizontal class="photo-list drop-zone" vn-droppable="$ctrl.onDrop($event)">
|
||||
<section class="empty-rows" ng-if="!$ctrl.photos.length">
|
||||
<section><vn-icon icon="image"></vn-icon></section>
|
||||
<section translate>Drag & Drop photos here...</section>
|
||||
</section>
|
||||
<section class="photo" ng-repeat="photo in $ctrl.photos">
|
||||
<section class="image vn-shadow" on-error-src
|
||||
ng-style="{'background': 'url(' + $ctrl.getImagePath(photo.dmsFk) + ')'}"
|
||||
zoom-image="{{$ctrl.getImagePath(photo.dmsFk)}}"
|
||||
ng-if="photo.dms.contentType != 'video/mp4'">
|
||||
</section>
|
||||
<video id="videobcg" muted="muted" controls ng-if="photo.dms.contentType == 'video/mp4'"
|
||||
class="video">
|
||||
<source src="{{$ctrl.getImagePath(photo.dmsFk)}}" type="video/mp4">
|
||||
</video>
|
||||
<section class="actions">
|
||||
<vn-button
|
||||
class="round"
|
||||
ng-click="confirm.show($index)"
|
||||
title="{{'Remove file' | translate}}"
|
||||
tabindex="-1"
|
||||
icon="delete">
|
||||
</vn-button>
|
||||
</section>
|
||||
</section>
|
||||
</vn-horizontal>
|
||||
<vn-confirm
|
||||
vn-id="confirm"
|
||||
message="This file will be deleted"
|
||||
question="Are you sure you want to continue?"
|
||||
on-accept="$ctrl.deleteDms($data)">
|
||||
</vn-confirm>
|
||||
<vn-float-button
|
||||
icon="add"
|
||||
vn-tooltip="Select file"
|
||||
vn-bind="+"
|
||||
ng-click="$ctrl.openUploadDialog()"
|
||||
fixed-bottom-right>
|
||||
</vn-float-button>
|
||||
|
||||
|
|
|
@ -1,105 +1,17 @@
|
|||
import ngModule from '../module';
|
||||
import Section from 'salix/components/section';
|
||||
import './style.scss';
|
||||
|
||||
class Controller extends Section {
|
||||
constructor($element, $, vnFile) {
|
||||
constructor($element, $) {
|
||||
super($element, $);
|
||||
this.vnFile = vnFile;
|
||||
this.filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'dms'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
deleteDms(index) {
|
||||
const dmsFk = this.photos[index].dmsFk;
|
||||
return this.$http.post(`ClaimDms/${dmsFk}/removeFile`)
|
||||
.then(() => {
|
||||
this.$.model.remove(index);
|
||||
this.vnApp.showSuccess(this.$t('File deleted'));
|
||||
});
|
||||
}
|
||||
|
||||
onDrop($event) {
|
||||
const files = $event.dataTransfer.files;
|
||||
this.setDefaultParams().then(() => {
|
||||
this.dms.files = files;
|
||||
this.create();
|
||||
});
|
||||
}
|
||||
|
||||
setDefaultParams() {
|
||||
const filter = {
|
||||
where: {code: 'claim'}
|
||||
};
|
||||
return this.$http.get('DmsTypes/findOne', {filter}).then(res => {
|
||||
const dmsTypeId = res.data && res.data.id;
|
||||
const companyId = this.vnConfig.companyFk;
|
||||
const warehouseId = this.vnConfig.warehouseFk;
|
||||
this.dms = {
|
||||
hasFile: false,
|
||||
hasFileAttached: false,
|
||||
reference: this.claim.id,
|
||||
warehouseId: warehouseId,
|
||||
companyId: companyId,
|
||||
dmsTypeId: dmsTypeId,
|
||||
description: this.$t('FileDescription', {
|
||||
claimId: this.claim.id,
|
||||
clientId: this.claim.client.id,
|
||||
clientName: this.claim.client.name
|
||||
}).toUpperCase()
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
openUploadDialog() {
|
||||
const element = document.createElement('input');
|
||||
element.setAttribute('type', 'file');
|
||||
element.setAttribute('multiple', true);
|
||||
element.click();
|
||||
|
||||
element.addEventListener('change', () =>
|
||||
this.setDefaultParams().then(() => {
|
||||
this.dms.files = element.files;
|
||||
this.create();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
create() {
|
||||
const query = `claims/${this.claim.id}/uploadFile`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: query,
|
||||
params: this.dms,
|
||||
headers: {'Content-Type': undefined},
|
||||
transformRequest: files => {
|
||||
const formData = new FormData();
|
||||
|
||||
for (let i = 0; i < files.length; i++)
|
||||
formData.append(files[i].name, files[i]);
|
||||
|
||||
return formData;
|
||||
},
|
||||
data: this.dms.files
|
||||
};
|
||||
this.$http(options).then(() => {
|
||||
this.vnApp.showSuccess(this.$t('File uploaded!'));
|
||||
this.$.model.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
getImagePath(dmsId) {
|
||||
return this.vnFile.getPath(`/api/Claims/${dmsId}/downloadFile`);
|
||||
async $onInit() {
|
||||
const url = await this.vnApp.getUrl(`claim/${this.$params.id}/photos`);
|
||||
window.location.href = url;
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$element', '$scope', 'vnFile'];
|
||||
|
||||
ngModule.vnComponent('vnClaimPhotos', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller,
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
import './index';
|
||||
import crudModel from 'core/mocks/crud-model';
|
||||
|
||||
describe('Claim', () => {
|
||||
describe('Component vnClaimPhotos', () => {
|
||||
let $scope;
|
||||
let $httpBackend;
|
||||
let controller;
|
||||
|
||||
beforeEach(ngModule('claim'));
|
||||
|
||||
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$scope = $rootScope.$new();
|
||||
controller = $componentController('vnClaimPhotos', {$element: null, $scope});
|
||||
controller.$.model = crudModel;
|
||||
controller.claim = {
|
||||
id: 1,
|
||||
client: {id: 1101, name: 'Bruce Wayne'}
|
||||
};
|
||||
}));
|
||||
|
||||
describe('deleteDms()', () => {
|
||||
it('should make an HTTP Post query', () => {
|
||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||
jest.spyOn(controller.$.model, 'remove');
|
||||
|
||||
const dmsId = 1;
|
||||
const dmsIndex = 0;
|
||||
controller.photos = [{dmsFk: 1}];
|
||||
|
||||
$httpBackend.expectPOST(`ClaimDms/${dmsId}/removeFile`).respond();
|
||||
controller.deleteDms(dmsIndex);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.$.model.remove).toHaveBeenCalledWith(dmsIndex);
|
||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setDefaultParams()', () => {
|
||||
it('should make an HTTP GET query, then set all dms properties', () => {
|
||||
$httpBackend.expectRoute('GET', `DmsTypes/findOne`).respond({});
|
||||
controller.setDefaultParams();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.dms).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('create()', () => {
|
||||
it('should make an HTTP Post query, then refresh the model data', () => {
|
||||
const claimId = 1;
|
||||
const dmsIndex = 0;
|
||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||
jest.spyOn(controller.$.model, 'refresh');
|
||||
controller.photos = [{dmsFk: 1}];
|
||||
controller.dmsIndex = dmsIndex;
|
||||
controller.dms = {files: []};
|
||||
|
||||
$httpBackend.expectPOST(`claims/${claimId}/uploadFile`).respond({});
|
||||
controller.create();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.$.model.refresh).toHaveBeenCalled();
|
||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
Are you sure you want to continue?: ¿Seguro que quieres continuar?
|
||||
Drag & Drop photos here...: Arrastra y suelta fotos aquí...
|
||||
File deleted: Archivo eliminado
|
||||
File uploaded!: Archivo subido!
|
||||
Select file: Seleccionar fichero
|
|
@ -1,47 +0,0 @@
|
|||
@import "./variables";
|
||||
|
||||
vn-claim-photos {
|
||||
height: 100%;
|
||||
|
||||
.drop-zone {
|
||||
color: $color-font-secondary;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
min-height: 100%;
|
||||
|
||||
.empty-rows {
|
||||
padding: 80px $spacing-md;
|
||||
font-size: 1.375rem
|
||||
}
|
||||
|
||||
vn-icon {
|
||||
font-size: 3rem
|
||||
}
|
||||
}
|
||||
|
||||
.photo-list {
|
||||
padding: $spacing-md;
|
||||
min-height: 100%;
|
||||
|
||||
.photo {
|
||||
width: 512px;
|
||||
height: 288px;
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),
|
||||
0 3px 1px -2px rgba(0,0,0,.2),
|
||||
0 1px 5px 0 rgba(0,0,0,.12);
|
||||
border: 2px solid transparent;
|
||||
|
||||
}
|
||||
.video:hover {
|
||||
border: 2px solid $color-primary
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
const {toCSV} = require('vn-loopback/util/csv');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('negativeBasesCsv', {
|
||||
description: 'Returns the negative bases as .csv',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'negativeBases',
|
||||
type: ['object'],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'from',
|
||||
type: 'date',
|
||||
description: 'From date'
|
||||
},
|
||||
{
|
||||
arg: 'to',
|
||||
type: 'date',
|
||||
description: 'To date'
|
||||
}],
|
||||
returns: [
|
||||
{
|
||||
arg: 'body',
|
||||
type: 'file',
|
||||
root: true
|
||||
}, {
|
||||
arg: 'Content-Type',
|
||||
type: 'String',
|
||||
http: {target: 'header'}
|
||||
}, {
|
||||
arg: 'Content-Disposition',
|
||||
type: 'String',
|
||||
http: {target: 'header'}
|
||||
}
|
||||
],
|
||||
http: {
|
||||
path: '/negativeBasesCsv',
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.negativeBasesCsv = async ctx => {
|
||||
const args = ctx.args;
|
||||
const content = toCSV(args.negativeBases);
|
||||
|
||||
return [
|
||||
content,
|
||||
'text/csv',
|
||||
`attachment; filename="negative-bases-${new Date(args.from).toLocaleDateString()}-${new Date(args.to).toLocaleDateString()}.csv"`
|
||||
];
|
||||
};
|
||||
};
|
|
@ -7,6 +7,4 @@ module.exports = Self => {
|
|||
require('../methods/invoice-in/invoiceInPdf')(Self);
|
||||
require('../methods/invoice-in/invoiceInEmail')(Self);
|
||||
require('../methods/invoice-in/getSerial')(Self);
|
||||
require('../methods/invoice-in/negativeBases')(Self);
|
||||
require('../methods/invoice-in/negativeBasesCsv')(Self);
|
||||
};
|
||||
|
|
|
@ -15,4 +15,3 @@ import './create';
|
|||
import './log';
|
||||
import './serial';
|
||||
import './serial-search-panel';
|
||||
import './negative-bases';
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
Has To Invoice: Facturar
|
||||
Download as CSV: Descargar como CSV
|
||||
company: Compañía
|
||||
country: País
|
||||
clientId: Id Cliente
|
||||
clientSocialName: Cliente
|
||||
amount: Importe
|
||||
taxableBase: Base
|
||||
ticketFk: Id Ticket
|
||||
isActive: Activo
|
||||
hasToInvoice: Facturar
|
||||
isTaxDataChecked: Datos comprobados
|
||||
comercialId: Id Comercial
|
||||
comercialName: Comercial
|
|
@ -10,8 +10,7 @@
|
|||
"menus": {
|
||||
"main": [
|
||||
{ "state": "invoiceIn.index", "icon": "icon-invoice-in"},
|
||||
{ "state": "invoiceIn.serial", "icon": "icon-invoice-in"},
|
||||
{ "state": "invoiceIn.negative-bases", "icon": "icon-ticket"}
|
||||
{ "state": "invoiceIn.serial", "icon": "icon-invoice-in"}
|
||||
],
|
||||
"card": [
|
||||
{
|
||||
|
@ -53,15 +52,6 @@
|
|||
"administrative"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "/negative-bases",
|
||||
"state": "invoiceIn.negative-bases",
|
||||
"component": "vn-negative-bases",
|
||||
"description": "Negative bases",
|
||||
"acl": [
|
||||
"administrative"
|
||||
]
|
||||
},
|
||||
{
|
||||
"url": "/serial",
|
||||
"state": "invoiceIn.serial",
|
||||
|
|
|
@ -96,16 +96,18 @@ module.exports = Self => {
|
|||
SELECT f.*
|
||||
FROM tmp.filter f`);
|
||||
|
||||
stmt.merge(conn.makeWhere(args.filter.where));
|
||||
stmt.merge(conn.makeOrderBy(args.filter.order));
|
||||
stmt.merge(conn.makeLimit(args.filter));
|
||||
if (args.filter) {
|
||||
stmt.merge(conn.makeWhere(args.filter.where));
|
||||
stmt.merge(conn.makeOrderBy(args.filter.order));
|
||||
stmt.merge(conn.makeLimit(args.filter));
|
||||
}
|
||||
|
||||
const negativeBasesIndex = stmts.push(stmt) - 1;
|
||||
|
||||
stmts.push(`DROP TEMPORARY TABLE tmp.filter, tmp.ticket, tmp.ticketTax, tmp.ticketAmount`);
|
||||
|
||||
const sql = ParameterizedSQL.join(stmts, ';');
|
||||
const result = await conn.executeStmt(sql, myOptions);
|
||||
const result = await conn.executeStmt(sql);
|
||||
|
||||
return negativeBasesIndex === 0 ? result : result[negativeBasesIndex];
|
||||
};
|
|
@ -0,0 +1,68 @@
|
|||
const {toCSV} = require('vn-loopback/util/csv');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('negativeBasesCsv', {
|
||||
description: 'Returns the negative bases as .csv',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'from',
|
||||
type: 'date',
|
||||
description: 'From date',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'to',
|
||||
type: 'date',
|
||||
description: 'To date',
|
||||
required: true
|
||||
}],
|
||||
returns: [
|
||||
{
|
||||
arg: 'body',
|
||||
type: 'file',
|
||||
root: true
|
||||
}, {
|
||||
arg: 'Content-Type',
|
||||
type: 'String',
|
||||
http: {target: 'header'}
|
||||
}, {
|
||||
arg: 'Content-Disposition',
|
||||
type: 'String',
|
||||
http: {target: 'header'}
|
||||
}
|
||||
],
|
||||
http: {
|
||||
path: '/negativeBasesCsv',
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.negativeBasesCsv = async(ctx, options) => {
|
||||
const $t = ctx.req.__; // $translate
|
||||
const args = ctx.args;
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const negativeBases = await Self.app.models.InvoiceOut.negativeBases(ctx, myOptions);
|
||||
const locatedFields = [];
|
||||
negativeBases.forEach(element => {
|
||||
locatedFields.push(Object.keys(element).map(key => {
|
||||
return {newName: $t(key), value: element[key]};
|
||||
}).filter(item => item !== null)
|
||||
.reduce((result, item) => {
|
||||
result[item.newName] = item.value;
|
||||
return result;
|
||||
}, {}));
|
||||
});
|
||||
const content = toCSV(locatedFields);
|
||||
|
||||
return [
|
||||
content,
|
||||
'text/csv',
|
||||
`attachment; filename="negative-bases-${new Date(args.from).toLocaleDateString()}-${new Date(args.to).toLocaleDateString()}.csv"`
|
||||
];
|
||||
};
|
||||
};
|
|
@ -1,19 +1,18 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('invoiceIn negativeBases()', () => {
|
||||
describe('invoiceOut negativeBases()', () => {
|
||||
it('should return all negative bases in a date range', async() => {
|
||||
const tx = await models.InvoiceIn.beginTransaction({});
|
||||
const tx = await models.InvoiceOut.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
const ctx = {
|
||||
args: {
|
||||
from: new Date().setMonth(new Date().getMonth() - 12),
|
||||
to: new Date(),
|
||||
filter: {}
|
||||
to: new Date()
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await models.InvoiceIn.negativeBases(ctx, options);
|
||||
const result = await models.InvoiceOut.negativeBases(ctx, options);
|
||||
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
|
||||
|
@ -26,7 +25,7 @@ describe('invoiceIn negativeBases()', () => {
|
|||
|
||||
it('should throw an error if a date range is not in args', async() => {
|
||||
let error;
|
||||
const tx = await models.InvoiceIn.beginTransaction({});
|
||||
const tx = await models.InvoiceOut.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
const ctx = {
|
||||
args: {
|
||||
|
@ -35,7 +34,7 @@ describe('invoiceIn negativeBases()', () => {
|
|||
};
|
||||
|
||||
try {
|
||||
await models.InvoiceIn.negativeBases(ctx, options);
|
||||
await models.InvoiceOut.negativeBases(ctx, options);
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
error = e;
|
|
@ -17,4 +17,6 @@ module.exports = Self => {
|
|||
require('../methods/invoiceOut/invoiceCsvEmail')(Self);
|
||||
require('../methods/invoiceOut/invoiceOutPdf')(Self);
|
||||
require('../methods/invoiceOut/getInvoiceDate')(Self);
|
||||
require('../methods/invoiceOut/negativeBases')(Self);
|
||||
require('../methods/invoiceOut/negativeBasesCsv')(Self);
|
||||
};
|
||||
|
|
|
@ -10,3 +10,4 @@ import './descriptor-popover';
|
|||
import './descriptor-menu';
|
||||
import './index/manual';
|
||||
import './global-invoicing';
|
||||
import './negative-bases';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="InvoiceIns/negativeBases"
|
||||
url="InvoiceOuts/negativeBases"
|
||||
auto-load="true"
|
||||
params="$ctrl.params"
|
||||
limit="20">
|
||||
|
@ -43,7 +43,7 @@
|
|||
<span translate>Country</span>
|
||||
</th>
|
||||
<th field="clientId">
|
||||
<span translate>Id Client</span>
|
||||
<span translate>Client id</span>
|
||||
</th>
|
||||
<th field="clientSocialName">
|
||||
<span translate>Client</span>
|
||||
|
@ -55,7 +55,7 @@
|
|||
<span translate>Base</span>
|
||||
</th>
|
||||
<th field="ticketFk">
|
||||
<span translate>Id Ticket</span>
|
||||
<span translate>Ticket id</span>
|
||||
</th>
|
||||
<th field="isActive">
|
||||
<span translate>Active</span>
|
|
@ -58,18 +58,7 @@ export default class Controller extends Section {
|
|||
}
|
||||
|
||||
downloadCSV() {
|
||||
const data = [];
|
||||
this.$.model._orgData.forEach(element => {
|
||||
data.push(Object.keys(element).map(key => {
|
||||
return {newName: this.$t(key), value: element[key]};
|
||||
}).filter(item => item !== null)
|
||||
.reduce((result, item) => {
|
||||
result[item.newName] = item.value;
|
||||
return result;
|
||||
}, {}));
|
||||
});
|
||||
this.vnReport.show('InvoiceIns/negativeBasesCsv', {
|
||||
negativeBases: data,
|
||||
this.vnReport.show('InvoiceOuts/negativeBasesCsv', {
|
||||
from: this.params.from,
|
||||
to: this.params.to
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
Has To Invoice: Facturar
|
||||
Download as CSV: Descargar como CSV
|
|
@ -7,8 +7,8 @@
|
|||
"menus": {
|
||||
"main": [
|
||||
{"state": "invoiceOut.index", "icon": "icon-invoice-out"},
|
||||
{"state": "invoiceOut.global-invoicing", "icon": "contact_support"}
|
||||
|
||||
{"state": "invoiceOut.global-invoicing", "icon": "contact_support"},
|
||||
{ "state": "invoiceOut.negative-bases", "icon": "icon-ticket"}
|
||||
]
|
||||
},
|
||||
"routes": [
|
||||
|
@ -46,6 +46,15 @@
|
|||
"state": "invoiceOut.card",
|
||||
"abstract": true,
|
||||
"component": "vn-invoice-out-card"
|
||||
},
|
||||
{
|
||||
"url": "/negative-bases",
|
||||
"state": "invoiceOut.negative-bases",
|
||||
"component": "vn-negative-bases",
|
||||
"description": "Negative bases",
|
||||
"acl": [
|
||||
"administrative"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -23,6 +23,6 @@ describe('Supplier filter()', () => {
|
|||
|
||||
let result = await app.models.Supplier.filter(ctx);
|
||||
|
||||
expect(result.length).toEqual(2);
|
||||
expect(result.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
margin-right: 2cm;
|
||||
font-size: 10px;
|
||||
color: #555;
|
||||
width: 100%;
|
||||
zoom: 0.65
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1 @@
|
|||
numPages: Page <span class="pageNumber"></span> of <span class="totalPages"></span>
|
||||
law:
|
||||
privacy: 'In compliance with the provisions of Organic Law 15/1999, on the
|
||||
Protection of Personal Data, we inform you that the personal data you provide
|
||||
will be included in automated files of VERDNATURA LEVANTE SL, being able at all
|
||||
times to exercise the rights of access, rectification, cancellation and opposition,
|
||||
communicating it in writing to the registered office of the entity.
|
||||
The purpose of the file is administrative management, accounting, and billing.'
|
||||
numPages: Page <span class="pageNumber"></span> of <span class="totalPages"></span>
|
|
@ -1,8 +1 @@
|
|||
numPages: Página <span class="pageNumber"></span> de <span class="totalPages"></span>
|
||||
law:
|
||||
privacy: En cumplimiento de lo dispuesto en la Ley Orgánica 15/1999, de Protección
|
||||
de Datos de Carácter Personal, le comunicamos que los datos personales que facilite
|
||||
se incluirán en ficheros automatizados de VERDNATURA LEVANTE S.L., pudiendo en
|
||||
todo momento ejercitar los derechos de acceso, rectificación, cancelación y oposición,
|
||||
comunicándolo por escrito al domicilio social de la entidad. La finalidad del
|
||||
fichero es la gestión administrativa, contabilidad, y facturación.
|
||||
numPages: Página <span class="pageNumber"></span> de <span class="totalPages"></span>
|
|
@ -1,8 +1 @@
|
|||
numPages: Page <span class="pageNumber"></span> de <span class="totalPages"></span>
|
||||
law:
|
||||
privacy: Conformément aux dispositions de la loi organique 15/1999 sur la protection
|
||||
des données personnelles, nous vous informons que les données personnelles que
|
||||
vous fournissez seront incluses dans des dossiers. VERDNATURA LEVANTE S.L., vous
|
||||
pouvez à tout moment, exercer les droits d'accès, de rectification, d'annulation
|
||||
et d'opposition, en communiquant par écrit au siège social de la société. Le dossier
|
||||
a pour objet la gestion administrative, la comptabilité et la facturation.
|
||||
|
|
|
@ -1,8 +1 @@
|
|||
numPages: Página <span class="pageNumber"></span> de <span class="totalPages"></span>
|
||||
law:
|
||||
privacy: Em cumprimento do disposto na lei Orgânica 15/1999, de Protecção de Dados
|
||||
de Carácter Pessoal, comunicamos que os dados pessoais que facilite se incluirão
|
||||
nos ficheiros automatizados de VERDNATURA LEVANTE S.L., podendo em todo momento
|
||||
exercer os direitos de acesso, rectificação, cancelação e oposição, comunicando
|
||||
por escrito ao domicílio social da entidade. A finalidade do ficheiro é a gestão
|
||||
administrativa, contabilidade e facturação.
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
<div class="centerText" v-if="centerText" class="uppercase">{{centerText}}</div>
|
||||
<div class="pageCount" v-html="$t('numPages')"></div>
|
||||
</div>
|
||||
<p class="privacy" v-html="$t('law.privacy')"></p>
|
||||
<p
|
||||
v-if="company?.footnotes"
|
||||
v-html="company.footnotes">
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,17 @@
|
|||
/* eslint-disable no-tabs */
|
||||
const db = require('../../database');
|
||||
|
||||
module.exports = {
|
||||
name: 'report-footer',
|
||||
props: ['leftText', 'centerText']
|
||||
async serverPrefetch() {
|
||||
this.company = await db.findOne(
|
||||
`SELECT
|
||||
ci.footnotes
|
||||
FROM companyI18n ci
|
||||
JOIN company c ON c.id = ci.companyFk
|
||||
WHERE c.code = ? AND ci.lang = (SELECT lang FROM account.user WHERE id = ?)`,
|
||||
[this.companyCode, this.recipientId]);
|
||||
},
|
||||
|
||||
props: ['leftText', 'companyCode', 'recipientId', 'centerText']
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
{{companyName}}. {{company.street}}.
|
||||
{{company.postCode}} {{company.city}}.
|
||||
☎ {{companyPhone}}
|
||||
· {{$t('company.contactData')}}
|
||||
· {{company.web}} - {{company.email}}
|
||||
</section>
|
||||
<section>CIF: {{fiscalAddress.nif}} {{fiscalAddress.register}}</section>
|
||||
</header>
|
||||
|
|
|
@ -43,7 +43,9 @@ module.exports = {
|
|||
s.postCode,
|
||||
s.city,
|
||||
s.phone,
|
||||
cg.code AS groupName
|
||||
cg.code AS groupName,
|
||||
c.email,
|
||||
c.web
|
||||
FROM company c
|
||||
JOIN companyGroup cg ON cg.id = c.companyGroupFk
|
||||
JOIN supplier s ON s.id = c.id
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
const Stylesheet = require(`vn-print/core/stylesheet`);
|
||||
|
||||
const path = require('path');
|
||||
const vnPrintPath = path.resolve('print');
|
||||
|
||||
module.exports = new Stylesheet([
|
||||
`${vnPrintPath}/common/css/spacing.css`,
|
||||
`${vnPrintPath}/common/css/misc.css`,
|
||||
`${vnPrintPath}/common/css/layout.css`,
|
||||
`${vnPrintPath}/common/css/email.css`])
|
||||
.mergeStyles();
|
|
@ -0,0 +1,15 @@
|
|||
<email-body v-bind="$props">
|
||||
<div class="grid-row">
|
||||
<div class="grid-block vn-px-ml centered">
|
||||
<h1>{{ $t('total') }}: {{tickets.length}}</h1>
|
||||
<hr>
|
||||
</div>
|
||||
<div v-for="ticket in tickets" class="grid-block vn-px-ml">
|
||||
<p v-if="ticket.ticketId"><b>{{ $t('ticketId') }}:</b> {{ticket.ticketId}}</p>
|
||||
<p><b>{{ $t('clientId') }}:</b> <a :href="clientGreugeUrl(ticket.clientId)" target="_blank">{{ticket.clientId}}</a></p>
|
||||
<p v-if="ticket.description"><b>{{ $t('description') }}:</b> {{ticket.description}}</p>
|
||||
<p><b>{{ $t('amount') }}:</b> {{ticket.amount}} €</p>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
</email-body>
|
|
@ -0,0 +1,36 @@
|
|||
const Component = require(`vn-print/core/component`);
|
||||
const emailBody = new Component('email-body');
|
||||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
module.exports = {
|
||||
name: 'greuge-wrong',
|
||||
async serverPrefetch() {
|
||||
this.url = await this.salixUrl();
|
||||
|
||||
if (!this.url)
|
||||
throw new Error('Something went wrong');
|
||||
},
|
||||
components: {
|
||||
'email-body': emailBody.build(),
|
||||
},
|
||||
methods: {
|
||||
async salixUrl() {
|
||||
const salix = await models.Url.findOne({
|
||||
where: {
|
||||
appName: 'salix',
|
||||
environment: process.env.NODE_ENV || 'dev'
|
||||
}
|
||||
});
|
||||
return salix.url;
|
||||
},
|
||||
clientGreugeUrl(clientId) {
|
||||
return `${this.url}client/${clientId}/greuge/index`
|
||||
},
|
||||
},
|
||||
props: {
|
||||
tickets: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
subject: Abnormal greuges have been created
|
||||
total: Total number of abnormal greuges
|
||||
ticketId: Ticket
|
||||
clientId: Client
|
||||
description: Description
|
||||
amount: Amount
|
|
@ -0,0 +1,6 @@
|
|||
subject: Se han creado greuges anormales
|
||||
total: Número total de greuges anormales
|
||||
ticketId: Ticket
|
||||
clientId: Cliente
|
||||
description: Descipción
|
||||
amount: Importe
|
|
@ -5,7 +5,7 @@
|
|||
</div>
|
||||
<report-header v-bind="$props" v-bind:company-code="invoice.companyCode"></report-header>
|
||||
</template>
|
||||
<div class="grid-row">
|
||||
<div class="grid-row">
|
||||
<div class="grid-block">
|
||||
<div class="columns vn-mb-lg">
|
||||
<div class="size50">
|
||||
|
@ -242,7 +242,7 @@
|
|||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<div class="columns vn-mt-xl" v-if="invoice.payMethodCode == 'wireTransfer' || ticketObservations">
|
||||
<div class="columns vn-mt-xl" v-if="invoice.payMethodCode == 'wireTransfer' && invoice.iban">
|
||||
<div class="size50 pull-left no-page-break">
|
||||
<div class="panel">
|
||||
<div class="header">{{$t('observations')}}</div>
|
||||
|
@ -266,7 +266,9 @@
|
|||
v-bind:company-code="invoice.companyCode"
|
||||
v-bind:left-text="$t('invoiceRef', [invoice.ref])"
|
||||
v-bind:center-text="client.socialName"
|
||||
v-bind:recipient-id="client.id"
|
||||
v-bind="$props"
|
||||
|
||||
>
|
||||
</report-footer>
|
||||
</template>
|
||||
|
|
|
@ -11,8 +11,12 @@ module.exports = {
|
|||
this.client = await this.findOneFromDef('client', [this.reference]);
|
||||
this.taxes = await this.rawSqlFromDef(`taxes`, [this.reference]);
|
||||
this.hasIntrastat = await this.findValueFromDef(`hasIntrastat`, [this.reference]);
|
||||
this.intrastat = await this.rawSqlFromDef(`intrastat`,
|
||||
[this.reference, this.reference, this.reference, this.reference]);
|
||||
this.intrastat = await this.rawSqlFromDef(`intrastat`, [
|
||||
this.reference,
|
||||
this.reference,
|
||||
this.reference,
|
||||
this.reference
|
||||
]);
|
||||
this.rectified = await this.rawSqlFromDef(`rectified`, [this.reference]);
|
||||
this.hasIncoterms = await this.findValueFromDef(`hasIncoterms`, [this.reference]);
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ FROM invoiceOut io
|
|||
JOIN client c ON c.id = io.clientFk
|
||||
JOIN payMethod pm ON pm.id = c.payMethodFk
|
||||
JOIN company cny ON cny.id = io.companyFk
|
||||
JOIN supplierAccount sa ON sa.id = cny.supplierAccountFk
|
||||
LEFT JOIN supplierAccount sa ON sa.id = cny.supplierAccountFk
|
||||
LEFT JOIN invoiceOutSerial ios ON ios.code = io.serial
|
||||
LEFT JOIN ticket t ON t.refFk = io.ref
|
||||
WHERE t.refFk = ?
|
Loading…
Reference in New Issue