Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 3108-item_isBargain_isOnOffer_deprecation
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
b41a0bd849
|
@ -0,0 +1,144 @@
|
||||||
|
DROP PROCEDURE IF EXISTS vn.item_getBalance;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE
|
||||||
|
definer = root@`%` procedure vn.item_getBalance(IN vItemId int, IN vWarehouse int)
|
||||||
|
BEGIN
|
||||||
|
DECLARE vDateInventory DATETIME;
|
||||||
|
DECLARE vCurdate DATE DEFAULT CURDATE();
|
||||||
|
DECLARE vDayEnd DATETIME DEFAULT util.dayEnd(vCurdate);
|
||||||
|
|
||||||
|
SELECT inventoried INTO vDateInventory FROM config;
|
||||||
|
SET @a = 0;
|
||||||
|
SET @currentLineFk = 0;
|
||||||
|
SET @shipped = '';
|
||||||
|
|
||||||
|
SELECT DATE(@shipped:= shipped) shipped,
|
||||||
|
alertLevel,
|
||||||
|
stateName,
|
||||||
|
origin,
|
||||||
|
reference,
|
||||||
|
clientFk,
|
||||||
|
name,
|
||||||
|
`in` AS invalue,
|
||||||
|
`out`,
|
||||||
|
@a := @a + IFNULL(`in`,0) - IFNULL(`out`,0) as balance,
|
||||||
|
@currentLineFk := IF (@shipped < CURDATE()
|
||||||
|
OR (@shipped = CURDATE() AND (isPicked OR alertLevel >= 2)),
|
||||||
|
lineFk,@currentLineFk) lastPreparedLineFk,
|
||||||
|
isTicket,
|
||||||
|
lineFk,
|
||||||
|
isPicked,
|
||||||
|
clientType,
|
||||||
|
claimFk
|
||||||
|
FROM
|
||||||
|
( SELECT tr.landed AS shipped,
|
||||||
|
b.quantity AS `in`,
|
||||||
|
NULL AS `out`,
|
||||||
|
al.id AS alertLevel,
|
||||||
|
st.name AS stateName,
|
||||||
|
s.name AS name,
|
||||||
|
e.ref AS reference,
|
||||||
|
e.id AS origin,
|
||||||
|
s.id AS clientFk,
|
||||||
|
IF(al.id = 3, TRUE, FALSE) isPicked,
|
||||||
|
FALSE AS isTicket,
|
||||||
|
b.id lineFk,
|
||||||
|
NULL `order`,
|
||||||
|
NULL AS clientType,
|
||||||
|
NULL AS claimFk
|
||||||
|
FROM buy b
|
||||||
|
JOIN entry e ON e.id = b.entryFk
|
||||||
|
JOIN travel tr ON tr.id = e.travelFk
|
||||||
|
JOIN supplier s ON s.id = e.supplierFk
|
||||||
|
JOIN alertLevel al ON al.id =
|
||||||
|
CASE
|
||||||
|
WHEN tr.shipped < CURDATE() THEN 3
|
||||||
|
WHEN tr.shipped = CURDATE() AND tr.isReceived = TRUE THEN 3
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
JOIN state st ON st.code = al.code
|
||||||
|
WHERE tr.landed >= vDateInventory
|
||||||
|
AND vWarehouse = tr.warehouseInFk
|
||||||
|
AND b.itemFk = vItemId
|
||||||
|
AND e.isInventory = FALSE
|
||||||
|
AND e.isRaid = FALSE
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT tr.shipped,
|
||||||
|
NULL,
|
||||||
|
b.quantity,
|
||||||
|
al.id,
|
||||||
|
st.name,
|
||||||
|
s.name,
|
||||||
|
e.ref,
|
||||||
|
e.id,
|
||||||
|
s.id,
|
||||||
|
IF(al.id = 3, TRUE, FALSE),
|
||||||
|
FALSE,
|
||||||
|
b.id,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
FROM buy b
|
||||||
|
JOIN entry e ON e.id = b.entryFk
|
||||||
|
JOIN travel tr ON tr.id = e.travelFk
|
||||||
|
JOIN warehouse w ON w.id = tr.warehouseOutFk
|
||||||
|
JOIN supplier s ON s.id = e.supplierFk
|
||||||
|
JOIN alertLevel al ON al.id =
|
||||||
|
CASE
|
||||||
|
WHEN tr.shipped < CURDATE() THEN 3
|
||||||
|
WHEN tr.shipped = CURDATE() AND tr.isReceived = TRUE THEN 3
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
JOIN state st ON st.code = al.code
|
||||||
|
WHERE tr.shipped >= vDateInventory
|
||||||
|
AND vWarehouse =tr.warehouseOutFk
|
||||||
|
AND s.id <> 4
|
||||||
|
AND b.itemFk = vItemId
|
||||||
|
AND e.isInventory = FALSE
|
||||||
|
AND w.isFeedStock = FALSE
|
||||||
|
AND e.isRaid = FALSE
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT DATE(t.shipped),
|
||||||
|
NULL,
|
||||||
|
s.quantity,
|
||||||
|
al.id,
|
||||||
|
st.name,
|
||||||
|
t.nickname,
|
||||||
|
t.refFk,
|
||||||
|
t.id,
|
||||||
|
t.clientFk,
|
||||||
|
stk.id,
|
||||||
|
TRUE,
|
||||||
|
s.id,
|
||||||
|
st.`order`,
|
||||||
|
ct.code,
|
||||||
|
cl.id
|
||||||
|
FROM sale s
|
||||||
|
JOIN ticket t ON t.id = s.ticketFk
|
||||||
|
LEFT JOIN ticketState ts ON ts.ticket = t.id
|
||||||
|
LEFT JOIN state st ON st.code = ts.code
|
||||||
|
JOIN client c ON c.id = t.clientFk
|
||||||
|
JOIN clientType ct ON ct.id = c.clientTypeFk
|
||||||
|
JOIN alertLevel al ON al.id =
|
||||||
|
CASE
|
||||||
|
WHEN t.shipped < curdate() THEN 3
|
||||||
|
WHEN t.shipped > util.dayEnd(curdate()) THEN 0
|
||||||
|
ELSE IFNULL(ts.alertLevel, 0)
|
||||||
|
END
|
||||||
|
LEFT JOIN state stPrep ON stPrep.`code` = 'PREPARED'
|
||||||
|
LEFT JOIN saleTracking stk ON stk.saleFk = s.id AND stk.stateFk = stPrep.id
|
||||||
|
LEFT JOIN claim cl ON cl.ticketFk = t.id
|
||||||
|
WHERE t.shipped >= vDateInventory
|
||||||
|
AND s.itemFk = vItemId
|
||||||
|
AND vWarehouse =t.warehouseFk
|
||||||
|
ORDER BY shipped, alertLevel DESC, isTicket, `order` DESC, isPicked DESC, `in` DESC, `out` DESC
|
||||||
|
) AS itemDiary;
|
||||||
|
|
||||||
|
END;
|
||||||
|
$$
|
||||||
|
DELIMITER ;
|
||||||
|
|
|
@ -47,6 +47,13 @@ export async function getBrowser() {
|
||||||
});
|
});
|
||||||
page = extendPage(page);
|
page = extendPage(page);
|
||||||
page.setDefaultTimeout(5000);
|
page.setDefaultTimeout(5000);
|
||||||
|
await page.addStyleTag({
|
||||||
|
content: `* {
|
||||||
|
transition: none!important;
|
||||||
|
animation: none!important;
|
||||||
|
}`
|
||||||
|
});
|
||||||
|
|
||||||
await page.goto(defaultURL, {waitUntil: 'load'});
|
await page.goto(defaultURL, {waitUntil: 'load'});
|
||||||
return {page, close: browser.close.bind(browser)};
|
return {page, close: browser.close.bind(browser)};
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ describe('InvoiceOut manual invoice path', () => {
|
||||||
it('should open the manual invoice form', async() => {
|
it('should open the manual invoice form', async() => {
|
||||||
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
|
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
|
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
|
||||||
|
await page.waitForTimeout(1000); // initialization of functionality takes about 1000ms to work
|
||||||
await page.waitToClick(selectors.invoiceOutIndex.createManualInvoice);
|
await page.waitToClick(selectors.invoiceOutIndex.createManualInvoice);
|
||||||
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);
|
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,8 +3,8 @@ import Popover from '../popover';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
export default class Menu extends Popover {
|
export default class Menu extends Popover {
|
||||||
show(parent) {
|
show(parent, direction) {
|
||||||
super.show(parent);
|
super.show(parent, direction);
|
||||||
this.windowEl.addEventListener('click', () => this.hide());
|
this.windowEl.addEventListener('click', () => this.hide());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
@import "./effects";
|
@import "./effects";
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
.vn-menu {
|
.vn-menu {
|
||||||
vn-item, .vn-item {
|
vn-item, .vn-item {
|
||||||
@extend %clickable;
|
@extend %clickable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vn-item.dropdown:after,
|
||||||
|
.vn-item.dropdown:after {
|
||||||
|
font-family: 'Material Icons';
|
||||||
|
content: 'keyboard_arrow_right';
|
||||||
|
position: absolute;
|
||||||
|
color: $color-spacer;
|
||||||
|
font-size: 1.5em;
|
||||||
|
right: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,15 @@ export default class Popover extends Popup {
|
||||||
* it is shown in a visible relative position to it.
|
* it is shown in a visible relative position to it.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement|Event} parent Overrides the parent property
|
* @param {HTMLElement|Event} parent Overrides the parent property
|
||||||
|
* @param {String} direction - Direction [left]
|
||||||
*/
|
*/
|
||||||
show(parent) {
|
show(parent, direction) {
|
||||||
if (parent instanceof Event)
|
if (parent instanceof Event)
|
||||||
parent = event.target;
|
parent = event.target;
|
||||||
|
|
||||||
if (parent) this.parent = parent;
|
if (parent) this.parent = parent;
|
||||||
|
if (direction) this.direction = direction;
|
||||||
|
|
||||||
super.show();
|
super.show();
|
||||||
this.content = this.popup.querySelector('.content');
|
this.content = this.popup.querySelector('.content');
|
||||||
this.$timeout(() => this.relocate(), 10);
|
this.$timeout(() => this.relocate(), 10);
|
||||||
|
@ -89,21 +92,40 @@ export default class Popover extends Popup {
|
||||||
let width = clamp(popoverRect.width, parentRect.width, maxWith);
|
let width = clamp(popoverRect.width, parentRect.width, maxWith);
|
||||||
let height = popoverRect.height;
|
let height = popoverRect.height;
|
||||||
|
|
||||||
let left = parentRect.left + parentRect.width / 2 - width / 2;
|
let left;
|
||||||
|
if (this.direction == 'left') {
|
||||||
|
left = parentRect.left + parentRect.width;
|
||||||
left = clamp(left, margin, maxRight - width);
|
left = clamp(left, margin, maxRight - width);
|
||||||
|
} else {
|
||||||
|
left = parentRect.left + parentRect.width / 2 - width / 2;
|
||||||
|
left = clamp(left, margin, maxRight - width);
|
||||||
|
}
|
||||||
|
|
||||||
let top = parentRect.top + parentRect.height + arrowOffset;
|
let top;
|
||||||
|
if (this.direction == 'left')
|
||||||
|
top = parentRect.top;
|
||||||
|
else
|
||||||
|
top = parentRect.top + parentRect.height + arrowOffset;
|
||||||
let showTop = top + height > maxBottom;
|
let showTop = top + height > maxBottom;
|
||||||
if (showTop) top = parentRect.top - height - arrowOffset;
|
if (showTop) top = parentRect.top - height - arrowOffset;
|
||||||
top = Math.max(top, margin);
|
top = Math.max(top, margin);
|
||||||
|
|
||||||
if (showTop)
|
if (this.direction == 'left')
|
||||||
|
arrowStyle.left = `0`;
|
||||||
|
else if (showTop)
|
||||||
arrowStyle.bottom = `0`;
|
arrowStyle.bottom = `0`;
|
||||||
else
|
else
|
||||||
arrowStyle.top = `0`;
|
arrowStyle.top = `0`;
|
||||||
|
|
||||||
let arrowLeft = (parentRect.left - left) + parentRect.width / 2;
|
let arrowLeft;
|
||||||
|
if (this.direction == 'left') {
|
||||||
|
arrowLeft = 0;
|
||||||
|
let arrowTop = arrowOffset;
|
||||||
|
arrowStyle.top = `${arrowTop}px`;
|
||||||
|
} else {
|
||||||
|
arrowLeft = (parentRect.left - left) + parentRect.width / 2;
|
||||||
arrowLeft = clamp(arrowLeft, arrowHeight, width - arrowHeight);
|
arrowLeft = clamp(arrowLeft, arrowHeight, width - arrowHeight);
|
||||||
|
}
|
||||||
arrowStyle.left = `${arrowLeft}px`;
|
arrowStyle.left = `${arrowLeft}px`;
|
||||||
|
|
||||||
style.top = `${top}px`;
|
style.top = `${top}px`;
|
||||||
|
|
|
@ -18,6 +18,18 @@ class Email {
|
||||||
return this.$http.get(`email/${template}`, {params})
|
return this.$http.get(`email/${template}`, {params})
|
||||||
.then(() => this.vnApp.showMessage(this.$t('Notification sent!')));
|
.then(() => this.vnApp.showMessage(this.$t('Notification sent!')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends an email displaying a notification when it's sent.
|
||||||
|
*
|
||||||
|
* @param {String} template The email report name
|
||||||
|
* @param {Object} params The email parameters
|
||||||
|
* @return {Promise} Promise resolved when it's sent
|
||||||
|
*/
|
||||||
|
sendCsv(template, params) {
|
||||||
|
return this.$http.get(`csv/${template}/send`, {params})
|
||||||
|
.then(() => this.vnApp.showMessage(this.$t('Notification sent!')));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Email.$inject = ['$http', '$translate', 'vnApp'];
|
Email.$inject = ['$http', '$translate', 'vnApp'];
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,21 @@ class Report {
|
||||||
const serializedParams = this.$httpParamSerializer(params);
|
const serializedParams = this.$httpParamSerializer(params);
|
||||||
window.open(`api/report/${report}?${serializedParams}`);
|
window.open(`api/report/${report}?${serializedParams}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a report in another window, automatically adds the authorization
|
||||||
|
* token to params.
|
||||||
|
*
|
||||||
|
* @param {String} report The report name
|
||||||
|
* @param {Object} params The report parameters
|
||||||
|
*/
|
||||||
|
showCsv(report, params) {
|
||||||
|
params = Object.assign({
|
||||||
|
authorization: this.vnToken.token
|
||||||
|
}, params);
|
||||||
|
const serializedParams = this.$httpParamSerializer(params);
|
||||||
|
window.open(`api/csv/${report}/download?${serializedParams}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Report.$inject = ['$httpParamSerializer', 'vnToken'];
|
Report.$inject = ['$httpParamSerializer', 'vnToken'];
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
icon="menu"
|
icon="menu"
|
||||||
class="show-menu"
|
class="show-menu"
|
||||||
ng-if="$ctrl.leftMenu"
|
ng-if="$ctrl.leftMenu"
|
||||||
ng-click="$ctrl.leftMenu.show()">
|
ng-click="$ctrl.leftMenu.toggle()">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
<div class="side start">
|
<div class="side start">
|
||||||
<a class="logo" ui-sref="home" title="{{'Home' | translate}}">
|
<a class="logo" ui-sref="home" title="{{'Home' | translate}}">
|
||||||
|
|
|
@ -48,6 +48,10 @@ vn-layout {
|
||||||
.show-menu {
|
.show-menu {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
& > .show-menu {
|
||||||
|
margin-right: 5px;
|
||||||
|
display: block
|
||||||
|
}
|
||||||
.vn-button {
|
.vn-button {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
font-size: 1.05rem;
|
font-size: 1.05rem;
|
||||||
|
@ -71,6 +75,10 @@ vn-layout {
|
||||||
& > .main-view {
|
& > .main-view {
|
||||||
padding-left: $menu-width;
|
padding-left: $menu-width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.shown > .main-view {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.right-menu {
|
&.right-menu {
|
||||||
& > vn-topbar > .end {
|
& > vn-topbar > .end {
|
||||||
|
@ -85,6 +93,8 @@ vn-layout {
|
||||||
}
|
}
|
||||||
& > .main-view {
|
& > .main-view {
|
||||||
padding-top: $topbar-height;
|
padding-top: $topbar-height;
|
||||||
|
|
||||||
|
transition: padding-left 200ms ease-out;
|
||||||
}
|
}
|
||||||
ui-view {
|
ui-view {
|
||||||
& > * {
|
& > * {
|
||||||
|
@ -134,7 +144,8 @@ vn-layout {
|
||||||
& > vn-topbar {
|
& > vn-topbar {
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
& > .main-view {
|
& > .main-view,
|
||||||
|
&.shown > .main-view {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,12 +52,18 @@ export default class SideMenu extends Component {
|
||||||
this.hide();
|
this.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
if (this.shown) this.hide();
|
||||||
|
else this.show();
|
||||||
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
if (this.shown) return;
|
if (this.shown) return;
|
||||||
this.shown = true;
|
this.shown = true;
|
||||||
this.handler = e => this.onEscape(e);
|
this.handler = e => this.onEscape(e);
|
||||||
this.$window.addEventListener('keydown', this.handler);
|
this.$window.addEventListener('keydown', this.handler);
|
||||||
this.stateHandler = this.$transitions.onStart({}, t => this.onTransition(t));
|
this.stateHandler = this.$transitions.onStart({}, t => this.onTransition(t));
|
||||||
|
this.layout.element.classList.add('shown');
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
|
@ -65,6 +71,7 @@ export default class SideMenu extends Component {
|
||||||
this.$window.removeEventListener('keydown', this.handler);
|
this.$window.removeEventListener('keydown', this.handler);
|
||||||
this.stateHandler();
|
this.stateHandler();
|
||||||
this.shown = false;
|
this.shown = false;
|
||||||
|
this.layout.element.classList.remove('shown');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,16 +14,20 @@ vn-side-menu > .menu {
|
||||||
box-shadow: 0 1px 3px $color-shadow;
|
box-shadow: 0 1px 3px $color-shadow;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
top: $topbar-height;
|
top: $topbar-height;
|
||||||
|
transition: transform 200ms ease-out;
|
||||||
|
|
||||||
&.left {
|
&.left {
|
||||||
left: 0;
|
left: 0
|
||||||
}
|
}
|
||||||
&.right {
|
&.right {
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.shown {
|
||||||
|
transform: translateZ(0) translateX(-$menu-width);
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $mobile-width) {
|
@media screen and (max-width: $mobile-width) {
|
||||||
transition: transform 200ms ease-out;
|
|
||||||
z-index: 15;
|
z-index: 15;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
label="Type"
|
label="Orientation"
|
||||||
ng-model="$ctrl.viewportType"
|
ng-model="$ctrl.viewportType"
|
||||||
data="$ctrl.viewportTypes"
|
data="$ctrl.viewportTypes"
|
||||||
selection="$ctrl.viewportSelection"
|
selection="$ctrl.viewportSelection"
|
||||||
|
|
|
@ -36,6 +36,18 @@ export default class UploadPhoto extends Component {
|
||||||
width: 1350,
|
width: 1350,
|
||||||
height: 900
|
height: 900
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: 'vertical',
|
||||||
|
description: this.$t('Vertical'),
|
||||||
|
viewport: {
|
||||||
|
width: 306.66,
|
||||||
|
height: 533.33
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
width: 460,
|
||||||
|
height: 800
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
this.viewportType = 'normal';
|
this.viewportType = 'normal';
|
||||||
|
@ -103,8 +115,17 @@ export default class UploadPhoto extends Component {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = e => this.editor.bind({url: e.target.result});
|
reader.onload = e => this.editor.bind({url: e.target.result});
|
||||||
reader.readAsDataURL(value);
|
reader.readAsDataURL(value);
|
||||||
} else if (this.uploadMethod == 'URL')
|
} else if (this.uploadMethod == 'URL') {
|
||||||
this.editor.bind({url: value});
|
const img = new Image();
|
||||||
|
img.crossOrigin = 'Anonymous';
|
||||||
|
img.src = value;
|
||||||
|
img.onload = () => this.editor.bind({url: value});
|
||||||
|
img.onerror = () => {
|
||||||
|
this.vnApp.showError(
|
||||||
|
this.$t(`This photo provider doesn't allow remote downloads`)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ Select an image: Selecciona una imagen
|
||||||
File name: Nombre del fichero
|
File name: Nombre del fichero
|
||||||
Rotate left: Girar a la izquierda
|
Rotate left: Girar a la izquierda
|
||||||
Rotate right: Girar a la derecha
|
Rotate right: Girar a la derecha
|
||||||
Panoramic: Panorámico
|
Panoramic: Panorámica
|
||||||
|
Orientation: Orientación
|
||||||
Select from computer: Seleccionar desde ordenador
|
Select from computer: Seleccionar desde ordenador
|
||||||
Import from external URL: Importar desde URL externa
|
Import from external URL: Importar desde URL externa
|
||||||
|
This photo provider doesn't allow remote downloads: Este proveedor de fotos no permite descargas remotas
|
|
@ -2,18 +2,49 @@
|
||||||
module="invoiceOut"
|
module="invoiceOut"
|
||||||
description="$ctrl.invoiceOut.ref">
|
description="$ctrl.invoiceOut.ref">
|
||||||
<slot-menu>
|
<slot-menu>
|
||||||
|
<vn-item class="dropdown"
|
||||||
|
vn-click-stop="showInvoiceMenu.show($event, 'left')"
|
||||||
|
name="showInvoicePdf"
|
||||||
|
translate>
|
||||||
|
Show invoice...
|
||||||
|
|
||||||
|
<vn-menu vn-id="showInvoiceMenu">
|
||||||
|
<vn-list>
|
||||||
<a class="vn-item"
|
<a class="vn-item"
|
||||||
href="api/InvoiceOuts/{{$ctrl.id}}/download?access_token={{$ctrl.vnToken.token}}"
|
href="api/InvoiceOuts/{{$ctrl.id}}/download?access_token={{$ctrl.vnToken.token}}"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
name="showInvoicePdf"
|
name="showInvoicePdf"
|
||||||
translate>
|
translate>
|
||||||
Show invoice PDF
|
Show as PDF
|
||||||
</a>
|
</a>
|
||||||
<vn-item
|
<vn-item
|
||||||
ng-click="invoiceConfirmation.show({email: $ctrl.invoiceOut.client.email})"
|
ng-click="$ctrl.showCsvInvoice()"
|
||||||
|
translate>
|
||||||
|
Show as CSV
|
||||||
|
</vn-item>
|
||||||
|
</vn-list>
|
||||||
|
</vn-menu>
|
||||||
|
</vn-item>
|
||||||
|
<vn-item class="dropdown"
|
||||||
|
vn-click-stop="sendInvoiceMenu.show($event, 'left')"
|
||||||
name="sendInvoice"
|
name="sendInvoice"
|
||||||
translate>
|
translate>
|
||||||
Send invoice PDF
|
Send invoice...
|
||||||
|
|
||||||
|
<vn-menu vn-id="sendInvoiceMenu">
|
||||||
|
<vn-list>
|
||||||
|
<vn-item
|
||||||
|
ng-click="sendPdfConfirmation.show({email: $ctrl.invoiceOut.client.email})"
|
||||||
|
translate>
|
||||||
|
Send PDF
|
||||||
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-click="sendCsvConfirmation.show({email: $ctrl.invoiceOut.client.email})"
|
||||||
|
translate>
|
||||||
|
Send CSV
|
||||||
|
</vn-item>
|
||||||
|
</vn-list>
|
||||||
|
</vn-menu>
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item
|
<vn-item
|
||||||
ng-click="deleteConfirmation.show()"
|
ng-click="deleteConfirmation.show()"
|
||||||
|
@ -104,15 +135,32 @@
|
||||||
message="Generate PDF invoice document">
|
message="Generate PDF invoice document">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
|
|
||||||
<!-- Send invoice confirmation popup -->
|
<!-- Send PDF invoice confirmation popup -->
|
||||||
<vn-dialog class="edit"
|
<vn-dialog
|
||||||
vn-id="invoiceConfirmation"
|
vn-id="sendPdfConfirmation"
|
||||||
on-accept="$ctrl.sendInvoice($data)"
|
on-accept="$ctrl.sendPdfInvoice($data)"
|
||||||
message="Send invoice PDF">
|
message="Send PDF invoice">
|
||||||
<tpl-body>
|
<tpl-body>
|
||||||
<span translate>Are you sure you want to send it?</span>
|
<span translate>Are you sure you want to send it?</span>
|
||||||
<vn-textfield vn-one
|
<vn-textfield vn-one
|
||||||
ng-model="invoiceConfirmation.data.email">
|
ng-model="sendPdfConfirmation.data.email">
|
||||||
|
</vn-textfield>
|
||||||
|
</tpl-body>
|
||||||
|
<tpl-buttons>
|
||||||
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
|
<button response="accept" translate>Confirm</button>
|
||||||
|
</tpl-buttons>
|
||||||
|
</vn-dialog>
|
||||||
|
|
||||||
|
<!-- Send CSV invoice confirmation popup -->
|
||||||
|
<vn-dialog
|
||||||
|
vn-id="sendCsvConfirmation"
|
||||||
|
on-accept="$ctrl.sendCsvInvoice($data)"
|
||||||
|
message="Send CSV invoice">
|
||||||
|
<tpl-body>
|
||||||
|
<span translate>Are you sure you want to send it?</span>
|
||||||
|
<vn-textfield vn-one
|
||||||
|
ng-model="sendCsvConfirmation.data.email">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</tpl-body>
|
</tpl-body>
|
||||||
<tpl-buttons>
|
<tpl-buttons>
|
||||||
|
|
|
@ -14,29 +14,6 @@ class Controller extends Descriptor {
|
||||||
return this.aclService.hasAny(['invoicing']);
|
return this.aclService.hasAny(['invoicing']);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteInvoiceOut() {
|
|
||||||
return this.$http.post(`InvoiceOuts/${this.id}/delete`)
|
|
||||||
.then(() => this.$state.go('invoiceOut.index'))
|
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut deleted')));
|
|
||||||
}
|
|
||||||
|
|
||||||
bookInvoiceOut() {
|
|
||||||
return this.$http.post(`InvoiceOuts/${this.invoiceOut.ref}/book`)
|
|
||||||
.then(() => this.$state.reload())
|
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut booked')));
|
|
||||||
}
|
|
||||||
|
|
||||||
createInvoicePdf() {
|
|
||||||
const invoiceId = this.invoiceOut.id;
|
|
||||||
return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`)
|
|
||||||
.then(() => this.reload())
|
|
||||||
.then(() => {
|
|
||||||
const snackbarMessage = this.$t(
|
|
||||||
`The invoice PDF document has been regenerated`);
|
|
||||||
this.vnApp.showSuccess(snackbarMessage);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get filter() {
|
get filter() {
|
||||||
if (this.invoiceOut)
|
if (this.invoiceOut)
|
||||||
return JSON.stringify({refFk: this.invoiceOut.ref});
|
return JSON.stringify({refFk: this.invoiceOut.ref});
|
||||||
|
@ -55,7 +32,7 @@ class Controller extends Descriptor {
|
||||||
}, {
|
}, {
|
||||||
relation: 'client',
|
relation: 'client',
|
||||||
scope: {
|
scope: {
|
||||||
fields: ['id', 'name']
|
fields: ['id', 'name', 'email']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -76,13 +53,51 @@ class Controller extends Descriptor {
|
||||||
// Prevents error when not defined
|
// Prevents error when not defined
|
||||||
}
|
}
|
||||||
|
|
||||||
sendInvoice($data) {
|
deleteInvoiceOut() {
|
||||||
|
return this.$http.post(`InvoiceOuts/${this.id}/delete`)
|
||||||
|
.then(() => this.$state.go('invoiceOut.index'))
|
||||||
|
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut deleted')));
|
||||||
|
}
|
||||||
|
|
||||||
|
bookInvoiceOut() {
|
||||||
|
return this.$http.post(`InvoiceOuts/${this.invoiceOut.ref}/book`)
|
||||||
|
.then(() => this.$state.reload())
|
||||||
|
.then(() => this.vnApp.showSuccess(this.$t('InvoiceOut booked')));
|
||||||
|
}
|
||||||
|
|
||||||
|
createPdfInvoice() {
|
||||||
|
const invoiceId = this.invoiceOut.id;
|
||||||
|
return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`)
|
||||||
|
.then(() => this.reload())
|
||||||
|
.then(() => {
|
||||||
|
const snackbarMessage = this.$t(
|
||||||
|
`The invoice PDF document has been regenerated`);
|
||||||
|
this.vnApp.showSuccess(snackbarMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showCsvInvoice() {
|
||||||
|
this.vnReport.showCsv('invoice', {
|
||||||
|
recipientId: this.invoiceOut.client.id,
|
||||||
|
invoiceId: this.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendPdfInvoice($data) {
|
||||||
return this.vnEmail.send('invoice', {
|
return this.vnEmail.send('invoice', {
|
||||||
recipientId: this.invoiceOut.client.id,
|
recipientId: this.invoiceOut.client.id,
|
||||||
recipient: $data.email,
|
recipient: $data.email,
|
||||||
invoiceId: this.id
|
invoiceId: this.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendCsvInvoice($data) {
|
||||||
|
return this.vnEmail.sendCsv('invoice', {
|
||||||
|
recipientId: this.invoiceOut.client.id,
|
||||||
|
recipient: $data.email,
|
||||||
|
invoiceId: this.id
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnInvoiceOutDescriptor', {
|
ngModule.vnComponent('vnInvoiceOutDescriptor', {
|
||||||
|
|
|
@ -3,30 +3,20 @@ import './index';
|
||||||
describe('vnInvoiceOutDescriptor', () => {
|
describe('vnInvoiceOutDescriptor', () => {
|
||||||
let controller;
|
let controller;
|
||||||
let $httpBackend;
|
let $httpBackend;
|
||||||
const invoiceOut = {id: 1};
|
let $httpParamSerializer;
|
||||||
|
const invoiceOut = {
|
||||||
|
id: 1,
|
||||||
|
client: {id: 1101}
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(ngModule('invoiceOut'));
|
beforeEach(ngModule('invoiceOut'));
|
||||||
|
|
||||||
beforeEach(inject(($componentController, _$httpBackend_) => {
|
beforeEach(inject(($componentController, _$httpParamSerializer_, _$httpBackend_) => {
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpParamSerializer = _$httpParamSerializer_;
|
||||||
controller = $componentController('vnInvoiceOutDescriptor', {$element: null});
|
controller = $componentController('vnInvoiceOutDescriptor', {$element: null});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('createInvoicePdf()', () => {
|
|
||||||
it('should make a query and show a success snackbar', () => {
|
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
|
||||||
|
|
||||||
controller.invoiceOut = invoiceOut;
|
|
||||||
|
|
||||||
$httpBackend.whenGET(`InvoiceOuts/${invoiceOut.id}`).respond();
|
|
||||||
$httpBackend.expectPOST(`InvoiceOuts/${invoiceOut.id}/createPdf`).respond();
|
|
||||||
controller.createInvoicePdf();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('loadData()', () => {
|
describe('loadData()', () => {
|
||||||
it(`should perform a get query to store the invoice in data into the controller`, () => {
|
it(`should perform a get query to store the invoice in data into the controller`, () => {
|
||||||
const id = 1;
|
const id = 1;
|
||||||
|
@ -39,4 +29,81 @@ describe('vnInvoiceOutDescriptor', () => {
|
||||||
expect(controller.invoiceOut).toEqual(response);
|
expect(controller.invoiceOut).toEqual(response);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('createPdfInvoice()', () => {
|
||||||
|
it('should make a query to the createPdf() endpoint and show a success snackbar', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
|
||||||
|
controller.invoiceOut = invoiceOut;
|
||||||
|
|
||||||
|
$httpBackend.whenGET(`InvoiceOuts/${invoiceOut.id}`).respond();
|
||||||
|
$httpBackend.expectPOST(`InvoiceOuts/${invoiceOut.id}/createPdf`).respond();
|
||||||
|
controller.createPdfInvoice();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('showCsvInvoice()', () => {
|
||||||
|
it('should make a query to the csv invoice download endpoint and show a message snackbar', () => {
|
||||||
|
jest.spyOn(window, 'open').mockReturnThis();
|
||||||
|
|
||||||
|
controller.invoiceOut = invoiceOut;
|
||||||
|
|
||||||
|
const expectedParams = {
|
||||||
|
invoiceId: invoiceOut.id,
|
||||||
|
recipientId: invoiceOut.client.id
|
||||||
|
};
|
||||||
|
const serializedParams = $httpParamSerializer(expectedParams);
|
||||||
|
const expectedPath = `api/csv/invoice/download?${serializedParams}`;
|
||||||
|
controller.showCsvInvoice();
|
||||||
|
|
||||||
|
expect(window.open).toHaveBeenCalledWith(expectedPath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sendPdfInvoice()', () => {
|
||||||
|
it('should make a query to the email invoice endpoint and show a message snackbar', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showMessage');
|
||||||
|
|
||||||
|
controller.invoiceOut = invoiceOut;
|
||||||
|
|
||||||
|
const $data = {email: 'brucebanner@gothamcity.com'};
|
||||||
|
const expectedParams = {
|
||||||
|
invoiceId: invoiceOut.id,
|
||||||
|
recipient: $data.email,
|
||||||
|
recipientId: invoiceOut.client.id
|
||||||
|
};
|
||||||
|
const serializedParams = $httpParamSerializer(expectedParams);
|
||||||
|
|
||||||
|
$httpBackend.expectGET(`email/invoice?${serializedParams}`).respond();
|
||||||
|
controller.sendPdfInvoice($data);
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showMessage).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sendCsvInvoice()', () => {
|
||||||
|
it('should make a query to the csv invoice send endpoint and show a message snackbar', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showMessage');
|
||||||
|
|
||||||
|
controller.invoiceOut = invoiceOut;
|
||||||
|
|
||||||
|
const $data = {email: 'brucebanner@gothamcity.com'};
|
||||||
|
const expectedParams = {
|
||||||
|
invoiceId: invoiceOut.id,
|
||||||
|
recipient: $data.email,
|
||||||
|
recipientId: invoiceOut.client.id
|
||||||
|
};
|
||||||
|
const serializedParams = $httpParamSerializer(expectedParams);
|
||||||
|
|
||||||
|
$httpBackend.expectGET(`csv/invoice/send?${serializedParams}`).respond();
|
||||||
|
controller.sendCsvInvoice($data);
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showMessage).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,8 +2,10 @@ Volume exceded: Volumen excedido
|
||||||
Volume: Volumen
|
Volume: Volumen
|
||||||
Client card: Ficha del cliente
|
Client card: Ficha del cliente
|
||||||
Invoice ticket list: Listado de tickets de la factura
|
Invoice ticket list: Listado de tickets de la factura
|
||||||
Show invoice PDF: Ver factura en PDF
|
Show invoice...: Ver factura...
|
||||||
Send invoice PDF: Enviar factura en PDF
|
Send invoice...: Enviar factura...
|
||||||
|
Send PDF invoice: Enviar factura en PDF
|
||||||
|
Send CSV invoice: Enviar factura en CSV
|
||||||
Delete Invoice: Eliminar factura
|
Delete Invoice: Eliminar factura
|
||||||
Clone Invoice: Clonar factura
|
Clone Invoice: Clonar factura
|
||||||
InvoiceOut deleted: Factura eliminada
|
InvoiceOut deleted: Factura eliminada
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
<vn-table model="model">
|
<vn-table model="model">
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
|
<vn-th shrink></vn-th>
|
||||||
<vn-th expand>Date</vn-th>
|
<vn-th expand>Date</vn-th>
|
||||||
<vn-th number order="DESC" shrink>Id</vn-th>
|
<vn-th number order="DESC" shrink>Id</vn-th>
|
||||||
<vn-th>State</vn-th>
|
<vn-th>State</vn-th>
|
||||||
|
@ -48,6 +49,14 @@
|
||||||
vn-repeat-last
|
vn-repeat-last
|
||||||
on-last="$ctrl.scrollToLine(sale.lastPreparedLineFk)"
|
on-last="$ctrl.scrollToLine(sale.lastPreparedLineFk)"
|
||||||
ng-attr-id="vnItemDiary-{{::sale.lineFk}}">
|
ng-attr-id="vnItemDiary-{{::sale.lineFk}}">
|
||||||
|
<vn-td shrink>
|
||||||
|
<a ui-sref="claim.card.basicData({id: sale.claimFk})">
|
||||||
|
<vn-icon icon="icon-claims"
|
||||||
|
ng-show="sale.claimFk"
|
||||||
|
vn-tooltip="{{::$ctrl.$t('Claim')}}: {{::sale.claimFk}}">
|
||||||
|
</vn-icon>
|
||||||
|
</a>
|
||||||
|
</vn-td>
|
||||||
<vn-td expand>
|
<vn-td expand>
|
||||||
<span class="chip"
|
<span class="chip"
|
||||||
ng-class="::{warning: $ctrl.today == sale.shipped}">
|
ng-class="::{warning: $ctrl.today == sale.shipped}">
|
||||||
|
|
|
@ -7,17 +7,26 @@
|
||||||
order="landed DESC, buyFk DESC"
|
order="landed DESC, buyFk DESC"
|
||||||
limit="20">
|
limit="20">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-card class="vn-mb-md vn-w-xl vn-pa-lg">
|
|
||||||
<vn-date-picker
|
<vn-card class="vn-w-md vn-pa-md">
|
||||||
vn-none
|
<vn-horizontal>
|
||||||
|
<vn-date-picker class="vn-pa-xs"
|
||||||
|
vn-one
|
||||||
label="Since"
|
label="Since"
|
||||||
ng-model="$ctrl.date">
|
ng-model="$ctrl.dateFrom">
|
||||||
</vn-date-picker>
|
</vn-date-picker>
|
||||||
|
<vn-date-picker class="vn-pa-xs"
|
||||||
|
vn-one
|
||||||
|
label="To"
|
||||||
|
ng-model="$ctrl.dateTo">
|
||||||
|
</vn-date-picker>
|
||||||
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
||||||
|
|
||||||
<vn-data-viewer
|
<vn-data-viewer
|
||||||
model="model"
|
model="model"
|
||||||
class="vn-mb-xl vn-w-xl">
|
class="vn-mb-xl vn-w-xl vn-pa-md">
|
||||||
<vn-card class="vn-pa-lg">
|
<vn-card class="vn-pa-lg">
|
||||||
<vn-vertical>
|
<vn-vertical>
|
||||||
<vn-table model="model">
|
<vn-table model="model">
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import Section from 'salix/components/section';
|
import Section from 'salix/components/section';
|
||||||
import './style.scss';
|
|
||||||
|
|
||||||
class Controller extends Section {
|
class Controller extends Section {
|
||||||
constructor($element, $) {
|
constructor($element, $) {
|
||||||
super($element, $);
|
super($element, $);
|
||||||
|
|
||||||
const from = new Date();
|
const from = new Date();
|
||||||
from.setDate(from.getDate() - 75);
|
from.setDate(from.getDate());
|
||||||
from.setHours(0, 0, 0, 0);
|
from.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
const to = new Date();
|
const to = new Date();
|
||||||
to.setDate(to.getDate() + 60);
|
to.setDate(to.getDate() + 10);
|
||||||
to.setHours(23, 59, 59, 59);
|
to.setHours(23, 59, 59, 59);
|
||||||
|
|
||||||
this.filter = {
|
this.filter = {
|
||||||
|
@ -22,19 +21,19 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this._date = from;
|
this._dateFrom = from;
|
||||||
|
this._dateTo = to;
|
||||||
}
|
}
|
||||||
|
|
||||||
set date(value) {
|
set dateFrom(value) {
|
||||||
this._date = value;
|
this._dateFrom = value;
|
||||||
|
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
|
|
||||||
const from = new Date(value);
|
const from = new Date(value);
|
||||||
from.setHours(0, 0, 0, 0);
|
from.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
const to = new Date();
|
const to = new Date(this._dateTo);
|
||||||
to.setDate(to.getDate() + 60);
|
|
||||||
to.setHours(23, 59, 59, 59);
|
to.setHours(23, 59, 59, 59);
|
||||||
|
|
||||||
this.filter.where.shipped = {
|
this.filter.where.shipped = {
|
||||||
|
@ -43,8 +42,29 @@ class Controller extends Section {
|
||||||
this.$.model.refresh();
|
this.$.model.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
get date() {
|
set dateTo(value) {
|
||||||
return this._date;
|
this._dateTo = value;
|
||||||
|
|
||||||
|
if (!value) return;
|
||||||
|
|
||||||
|
const from = new Date(this._dateFrom);
|
||||||
|
from.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const to = new Date(value);
|
||||||
|
to.setHours(23, 59, 59, 59);
|
||||||
|
|
||||||
|
this.filter.where.shipped = {
|
||||||
|
between: [from, to]
|
||||||
|
};
|
||||||
|
this.$.model.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
get dateFrom() {
|
||||||
|
return this._dateFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
get dateTo() {
|
||||||
|
return this._dateTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
exprBuilder(param, value) {
|
exprBuilder(param, value) {
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
@import "variables";
|
|
||||||
|
|
||||||
vn-item-last-entries {
|
|
||||||
.round {
|
|
||||||
background-color: $color-spacer;
|
|
||||||
border-radius: 25px;
|
|
||||||
float: right;
|
|
||||||
width: 25px;
|
|
||||||
color: $color-font-dark;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
vn-horizontal {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
vn-date-picker {
|
|
||||||
flex: none !important;
|
|
||||||
}
|
|
||||||
@media screen and (max-width: 1440px) {
|
|
||||||
.expendable {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@
|
||||||
icon="more_vert"
|
icon="more_vert"
|
||||||
vn-popover="menu">
|
vn-popover="menu">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
|
|
||||||
<vn-menu vn-id="menu">
|
<vn-menu vn-id="menu">
|
||||||
<vn-list>
|
<vn-list>
|
||||||
<vn-item
|
<vn-item
|
||||||
|
@ -12,15 +13,44 @@
|
||||||
translate>
|
translate>
|
||||||
Add turn
|
Add turn
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item
|
<vn-item class="dropdown"
|
||||||
ng-click="$ctrl.showDeliveryNote()"
|
vn-click-stop="showDeliveryNoteMenu.show($event, 'left')"
|
||||||
translate>
|
translate>
|
||||||
Show Delivery Note
|
Show Delivery Note...
|
||||||
|
<vn-menu vn-id="showDeliveryNoteMenu">
|
||||||
|
<vn-list>
|
||||||
|
<vn-item
|
||||||
|
ng-click="$ctrl.showPdfDeliveryNote()"
|
||||||
|
translate>
|
||||||
|
Show as PDF
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item
|
<vn-item
|
||||||
ng-click="confirmDeliveryNote.show()"
|
ng-click="$ctrl.showCsvDeliveryNote()"
|
||||||
translate>
|
translate>
|
||||||
Send Delivery Note
|
Show as CSV
|
||||||
|
</vn-item>
|
||||||
|
</vn-list>
|
||||||
|
</vn-menu>
|
||||||
|
</vn-item>
|
||||||
|
<vn-item class="dropdown"
|
||||||
|
vn-click-stop="sendDeliveryNoteMenu.show($event, 'left')"
|
||||||
|
translate>
|
||||||
|
Send Delivery Note...
|
||||||
|
|
||||||
|
<vn-menu vn-id="sendDeliveryNoteMenu">
|
||||||
|
<vn-list>
|
||||||
|
<vn-item
|
||||||
|
ng-click="sendPdfConfirmation.show({email: $ctrl.ticket.client.email})"
|
||||||
|
translate>
|
||||||
|
Send PDF
|
||||||
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-click="sendCsvConfirmation.show({email: $ctrl.ticket.client.email})"
|
||||||
|
translate>
|
||||||
|
Send CSV
|
||||||
|
</vn-item>
|
||||||
|
</vn-list>
|
||||||
|
</vn-menu>
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item
|
<vn-item
|
||||||
ng-click="deleteConfirmation.show()"
|
ng-click="deleteConfirmation.show()"
|
||||||
|
@ -79,7 +109,7 @@
|
||||||
Make invoice
|
Make invoice
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item
|
<vn-item
|
||||||
ng-click="createInvoicePdfConfirmation.show()"
|
ng-click="createPdfConfirmation.show()"
|
||||||
ng-show="$ctrl.isInvoiced && ($ctrl.hasInvoicing || !$ctrl.ticket.invoiceOut.hasPdf)"
|
ng-show="$ctrl.isInvoiced && ($ctrl.hasInvoicing || !$ctrl.ticket.invoiceOut.hasPdf)"
|
||||||
name="regenerateInvoice"
|
name="regenerateInvoice"
|
||||||
translate>
|
translate>
|
||||||
|
@ -133,13 +163,39 @@
|
||||||
</div>
|
</div>
|
||||||
</vn-popup>
|
</vn-popup>
|
||||||
|
|
||||||
<!-- Send delivery note confirmation popup -->
|
<!-- Send PDF delivery note confirmation popup -->
|
||||||
<vn-confirm
|
<vn-dialog
|
||||||
vn-id="confirmDeliveryNote"
|
vn-id="sendPdfConfirmation"
|
||||||
on-accept="$ctrl.sendDeliveryNote()"
|
on-accept="$ctrl.sendPdfDeliveryNote($data)"
|
||||||
question="Are you sure you want to send it?"
|
message="Send PDF Delivery Note">
|
||||||
message="Send Delivery Note">
|
<tpl-body>
|
||||||
</vn-confirm>
|
<span translate>Are you sure you want to send it?</span>
|
||||||
|
<vn-textfield vn-one
|
||||||
|
ng-model="sendPdfConfirmation.data.email">
|
||||||
|
</vn-textfield>
|
||||||
|
</tpl-body>
|
||||||
|
<tpl-buttons>
|
||||||
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
|
<button response="accept" translate>Confirm</button>
|
||||||
|
</tpl-buttons>
|
||||||
|
</vn-dialog>
|
||||||
|
|
||||||
|
<!-- Send CSV delivery note confirmation popup -->
|
||||||
|
<vn-dialog
|
||||||
|
vn-id="sendCsvConfirmation"
|
||||||
|
on-accept="$ctrl.sendCsvDeliveryNote($data)"
|
||||||
|
message="Send CSV Delivery Note">
|
||||||
|
<tpl-body>
|
||||||
|
<span translate>Are you sure you want to send it?</span>
|
||||||
|
<vn-textfield vn-one
|
||||||
|
ng-model="sendCsvConfirmation.data.email">
|
||||||
|
</vn-textfield>
|
||||||
|
</tpl-body>
|
||||||
|
<tpl-buttons>
|
||||||
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
|
<button response="accept" translate>Confirm</button>
|
||||||
|
</tpl-buttons>
|
||||||
|
</vn-dialog>
|
||||||
|
|
||||||
<!-- Delete ticket confirmation popup -->
|
<!-- Delete ticket confirmation popup -->
|
||||||
<vn-confirm
|
<vn-confirm
|
||||||
|
@ -206,8 +262,8 @@
|
||||||
|
|
||||||
<!-- Create invoice PDF confirmation dialog -->
|
<!-- Create invoice PDF confirmation dialog -->
|
||||||
<vn-confirm
|
<vn-confirm
|
||||||
vn-id="createInvoicePdfConfirmation"
|
vn-id="createPdfConfirmation"
|
||||||
on-accept="$ctrl.createInvoicePdf()"
|
on-accept="$ctrl.createPdfInvoice()"
|
||||||
question="Are you sure you want to generate/regenerate the PDF invoice?"
|
question="Are you sure you want to generate/regenerate the PDF invoice?"
|
||||||
message="Generate PDF invoice document">
|
message="Generate PDF invoice document">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
|
|
|
@ -115,17 +115,32 @@ class Controller extends Section {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
showDeliveryNote() {
|
showPdfDeliveryNote() {
|
||||||
this.vnReport.show('delivery-note', {
|
this.vnReport.show('delivery-note', {
|
||||||
recipientId: this.ticket.client.id,
|
recipientId: this.ticket.client.id,
|
||||||
ticketId: this.id,
|
ticketId: this.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendDeliveryNote() {
|
showCsvDeliveryNote() {
|
||||||
|
this.vnReport.showCsv('delivery-note', {
|
||||||
|
recipientId: this.ticket.client.id,
|
||||||
|
ticketId: this.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendPdfDeliveryNote($data) {
|
||||||
return this.vnEmail.send('delivery-note', {
|
return this.vnEmail.send('delivery-note', {
|
||||||
recipientId: this.ticket.client.id,
|
recipientId: this.ticket.client.id,
|
||||||
recipient: this.ticket.client.email,
|
recipient: $data.email,
|
||||||
|
ticketId: this.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendCsvDeliveryNote($data) {
|
||||||
|
return this.vnEmail.sendCsv('delivery-note', {
|
||||||
|
recipientId: this.ticket.client.id,
|
||||||
|
recipient: $data.email,
|
||||||
ticketId: this.id
|
ticketId: this.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -227,7 +242,7 @@ class Controller extends Section {
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('Ticket invoiced')));
|
.then(() => this.vnApp.showSuccess(this.$t('Ticket invoiced')));
|
||||||
}
|
}
|
||||||
|
|
||||||
createInvoicePdf() {
|
createPdfInvoice() {
|
||||||
const invoiceId = this.ticket.invoiceOut.id;
|
const invoiceId = this.ticket.invoiceOut.id;
|
||||||
return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`)
|
return this.$http.post(`InvoiceOuts/${invoiceId}/createPdf`)
|
||||||
.then(() => this.reload())
|
.then(() => this.reload())
|
||||||
|
|
|
@ -2,6 +2,7 @@ import './index.js';
|
||||||
|
|
||||||
describe('Ticket Component vnTicketDescriptorMenu', () => {
|
describe('Ticket Component vnTicketDescriptorMenu', () => {
|
||||||
let $httpBackend;
|
let $httpBackend;
|
||||||
|
let $httpParamSerializer;
|
||||||
let controller;
|
let controller;
|
||||||
let $state;
|
let $state;
|
||||||
|
|
||||||
|
@ -25,8 +26,9 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
|
||||||
|
|
||||||
beforeEach(ngModule('ticket'));
|
beforeEach(ngModule('ticket'));
|
||||||
|
|
||||||
beforeEach(inject(($componentController, _$httpBackend_, _$state_) => {
|
beforeEach(inject(($componentController, _$httpBackend_, _$httpParamSerializer_, _$state_) => {
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpParamSerializer = _$httpParamSerializer_;
|
||||||
$state = _$state_;
|
$state = _$state_;
|
||||||
$state.params.id = 16;
|
$state.params.id = 16;
|
||||||
$state.getCurrentPath = () => [null, {state: {name: 'ticket'}}];
|
$state.getCurrentPath = () => [null, {state: {name: 'ticket'}}];
|
||||||
|
@ -104,36 +106,74 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('showDeliveryNote()', () => {
|
describe('showPdfDeliveryNote()', () => {
|
||||||
it('should open a new window showing a delivery note PDF document', () => {
|
it('should open a new window showing a delivery note PDF document', () => {
|
||||||
jest.spyOn(controller.vnReport, 'show');
|
jest.spyOn(window, 'open').mockReturnThis();
|
||||||
|
|
||||||
window.open = jasmine.createSpy('open');
|
const expectedParams = {
|
||||||
const params = {
|
ticketId: ticket.id,
|
||||||
recipientId: ticket.client.id,
|
recipientId: ticket.client.id
|
||||||
ticketId: ticket.id
|
|
||||||
};
|
};
|
||||||
controller.showDeliveryNote();
|
const serializedParams = $httpParamSerializer(expectedParams);
|
||||||
|
const expectedPath = `api/report/delivery-note?${serializedParams}`;
|
||||||
|
controller.showPdfDeliveryNote();
|
||||||
|
|
||||||
expect(controller.vnReport.show).toHaveBeenCalledWith('delivery-note', params);
|
expect(window.open).toHaveBeenCalledWith(expectedPath);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sendDeliveryNote()', () => {
|
describe('sendPdfDeliveryNote()', () => {
|
||||||
it('should make a query and call vnApp.showMessage()', () => {
|
it('should make a query and call vnApp.showMessage()', () => {
|
||||||
jest.spyOn(controller.vnEmail, 'send');
|
jest.spyOn(controller.vnEmail, 'send');
|
||||||
|
|
||||||
|
const $data = {email: 'brucebanner@gothamcity.com'};
|
||||||
const params = {
|
const params = {
|
||||||
recipient: ticket.client.email,
|
recipient: $data.email,
|
||||||
recipientId: ticket.client.id,
|
recipientId: ticket.client.id,
|
||||||
ticketId: ticket.id
|
ticketId: ticket.id
|
||||||
};
|
};
|
||||||
controller.sendDeliveryNote();
|
controller.sendPdfDeliveryNote($data);
|
||||||
|
|
||||||
expect(controller.vnEmail.send).toHaveBeenCalledWith('delivery-note', params);
|
expect(controller.vnEmail.send).toHaveBeenCalledWith('delivery-note', params);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('showCsvDeliveryNote()', () => {
|
||||||
|
it('should make a query to the csv delivery-note download endpoint and show a message snackbar', () => {
|
||||||
|
jest.spyOn(window, 'open').mockReturnThis();
|
||||||
|
|
||||||
|
const expectedParams = {
|
||||||
|
ticketId: ticket.id,
|
||||||
|
recipientId: ticket.client.id
|
||||||
|
};
|
||||||
|
const serializedParams = $httpParamSerializer(expectedParams);
|
||||||
|
const expectedPath = `api/csv/delivery-note/download?${serializedParams}`;
|
||||||
|
controller.showCsvDeliveryNote();
|
||||||
|
|
||||||
|
expect(window.open).toHaveBeenCalledWith(expectedPath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sendCsvDeliveryNote()', () => {
|
||||||
|
it('should make a query to the csv delivery-note send endpoint and show a message snackbar', () => {
|
||||||
|
jest.spyOn(controller.vnApp, 'showMessage');
|
||||||
|
|
||||||
|
const $data = {email: 'brucebanner@gothamcity.com'};
|
||||||
|
const expectedParams = {
|
||||||
|
ticketId: ticket.id,
|
||||||
|
recipient: $data.email,
|
||||||
|
recipientId: ticket.client.id,
|
||||||
|
};
|
||||||
|
const serializedParams = $httpParamSerializer(expectedParams);
|
||||||
|
|
||||||
|
$httpBackend.expectGET(`csv/delivery-note/send?${serializedParams}`).respond();
|
||||||
|
controller.sendCsvDeliveryNote($data);
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showMessage).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('makeInvoice()', () => {
|
describe('makeInvoice()', () => {
|
||||||
it('should make a query and call $state.reload() method', () => {
|
it('should make a query and call $state.reload() method', () => {
|
||||||
jest.spyOn(controller, 'reload').mockReturnThis();
|
jest.spyOn(controller, 'reload').mockReturnThis();
|
||||||
|
@ -149,13 +189,13 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createInvoicePdf()', () => {
|
describe('createPdfInvoice()', () => {
|
||||||
it('should make a query and show a success snackbar', () => {
|
it('should make a query and show a success snackbar', () => {
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
|
||||||
$httpBackend.whenGET(`Tickets/16`).respond();
|
$httpBackend.whenGET(`Tickets/16`).respond();
|
||||||
$httpBackend.expectPOST(`InvoiceOuts/${ticket.invoiceOut.id}/createPdf`).respond();
|
$httpBackend.expectPOST(`InvoiceOuts/${ticket.invoiceOut.id}/createPdf`).respond();
|
||||||
controller.createInvoicePdf();
|
controller.createPdfInvoice();
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
Show Delivery Note...: Ver albarán...
|
||||||
|
Send Delivery Note...: Enviar albarán...
|
||||||
|
Show as PDF: Ver como PDF
|
||||||
|
Show as CSV: Ver como CSV
|
||||||
|
Send PDF: Enviar PDF
|
||||||
|
Send CSV: Enviar CSV
|
||||||
|
Send CSV Delivery Note: Enviar albarán en CSV
|
||||||
|
Send PDF Delivery Note: Enviar albarán en PDF
|
|
@ -8,8 +8,6 @@ Add stowaway: Añadir polizón
|
||||||
Delete stowaway: Eliminar polizón
|
Delete stowaway: Eliminar polizón
|
||||||
Are you sure you want to delete this stowaway?: ¿Seguro que quieres eliminar este polizón?
|
Are you sure you want to delete this stowaway?: ¿Seguro que quieres eliminar este polizón?
|
||||||
Are you sure you want to send it?: ¿Seguro que quieres enviarlo?
|
Are you sure you want to send it?: ¿Seguro que quieres enviarlo?
|
||||||
Show Delivery Note: Ver albarán
|
|
||||||
Send Delivery Note: Enviar albarán
|
|
||||||
Show pallet report: Ver hoja de pallet
|
Show pallet report: Ver hoja de pallet
|
||||||
Change shipped hour: Cambiar hora de envío
|
Change shipped hour: Cambiar hora de envío
|
||||||
Shipped hour: Hora de envío
|
Shipped hour: Hora de envío
|
||||||
|
|
|
@ -37,20 +37,30 @@ class Email extends Component {
|
||||||
return userTranslations.subject;
|
return userTranslations.subject;
|
||||||
}
|
}
|
||||||
|
|
||||||
async send() {
|
/**
|
||||||
|
* @param {Object} [options] - Additional options
|
||||||
|
* @param {Boolean} [options.overrideAttachments] - Overrides default PDF attachments
|
||||||
|
* @param {Array} [options.attachments] - Array containing attachment objects
|
||||||
|
* @return {Promise} SMTP Promise
|
||||||
|
*/
|
||||||
|
async send(options = {}) {
|
||||||
const instance = this.build();
|
const instance = this.build();
|
||||||
const rendered = await this.render();
|
const rendered = await this.render();
|
||||||
const attachments = [];
|
const attachments = [];
|
||||||
const getAttachments = async(componentPath, files) => {
|
const getAttachments = async(componentPath, files) => {
|
||||||
for (file of files) {
|
for (file of files) {
|
||||||
const fileCopy = Object.assign({}, file);
|
const fileCopy = Object.assign({}, file);
|
||||||
|
const fileName = fileCopy.filename;
|
||||||
|
|
||||||
|
if (options.overrideAttachments && !fileName.includes('.png')) continue;
|
||||||
|
|
||||||
if (fileCopy.cid) {
|
if (fileCopy.cid) {
|
||||||
const templatePath = `${componentPath}/${file.path}`;
|
const templatePath = `${componentPath}/${file.path}`;
|
||||||
const fullFilePath = path.resolve(__dirname, templatePath);
|
const fullFilePath = path.resolve(__dirname, templatePath);
|
||||||
|
|
||||||
fileCopy.path = path.resolve(__dirname, fullFilePath);
|
fileCopy.path = path.resolve(__dirname, fullFilePath);
|
||||||
} else {
|
} else {
|
||||||
const reportName = fileCopy.filename.replace('.pdf', '');
|
const reportName = fileName.replace('.pdf', '');
|
||||||
const report = new Report(reportName, this.args);
|
const report = new Report(reportName, this.args);
|
||||||
fileCopy.content = await report.toPdfStream();
|
fileCopy.content = await report.toPdfStream();
|
||||||
}
|
}
|
||||||
|
@ -71,9 +81,14 @@ class Email extends Component {
|
||||||
if (this.attachments)
|
if (this.attachments)
|
||||||
await getAttachments(this.path, this.attachments);
|
await getAttachments(this.path, this.attachments);
|
||||||
|
|
||||||
|
if (options.attachments) {
|
||||||
|
for (let attachment of options.attachments)
|
||||||
|
attachments.push(attachment);
|
||||||
|
}
|
||||||
|
|
||||||
const localeSubject = await this.getSubject();
|
const localeSubject = await this.getSubject();
|
||||||
const replyTo = this.args.replyTo || this.args.auth.email;
|
const replyTo = this.args.replyTo || this.args.auth.email;
|
||||||
const options = {
|
const mailOptions = {
|
||||||
to: this.args.recipient,
|
to: this.args.recipient,
|
||||||
replyTo: replyTo,
|
replyTo: replyTo,
|
||||||
subject: localeSubject,
|
subject: localeSubject,
|
||||||
|
@ -81,7 +96,7 @@ class Email extends Component {
|
||||||
attachments: attachments
|
attachments: attachments
|
||||||
};
|
};
|
||||||
|
|
||||||
return smtp.send(options);
|
return smtp.send(mailOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,10 @@ module.exports = app => {
|
||||||
const methods = [];
|
const methods = [];
|
||||||
|
|
||||||
// Get all methods
|
// Get all methods
|
||||||
methodsDir.forEach(method => {
|
for (let method of methodsDir) {
|
||||||
|
if (method.includes('.js'))
|
||||||
methods.push(method.replace('.js', ''));
|
methods.push(method.replace('.js', ''));
|
||||||
});
|
}
|
||||||
|
|
||||||
// Auth middleware
|
// Auth middleware
|
||||||
const paths = [];
|
const paths = [];
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
module.exports = app => {
|
||||||
|
app.use('/api/csv/delivery-note', require('./csv/delivery-note')(app));
|
||||||
|
app.use('/api/csv/invoice', require('./csv/invoice')(app));
|
||||||
|
|
||||||
|
app.toCSV = function toCSV(rows) {
|
||||||
|
const [columns] = rows;
|
||||||
|
let content = Object.keys(columns).join('\t');
|
||||||
|
for (let row of rows) {
|
||||||
|
const values = Object.values(row);
|
||||||
|
const finalValues = values.map(value => {
|
||||||
|
if (value instanceof Date) return formatDate(value);
|
||||||
|
if (value === null) return '';
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
content += '\n';
|
||||||
|
content += finalValues.join('\t');
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatDate(date) {
|
||||||
|
return new Intl.DateTimeFormat('es', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'numeric',
|
||||||
|
day: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit'
|
||||||
|
}).format(date);
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,82 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = new express.Router();
|
||||||
|
const path = require('path');
|
||||||
|
const db = require('../../../core/database');
|
||||||
|
const sqlPath = path.join(__dirname, 'sql');
|
||||||
|
|
||||||
|
module.exports = app => {
|
||||||
|
router.get('/preview', async function(req, res, next) {
|
||||||
|
try {
|
||||||
|
const reqArgs = req.args;
|
||||||
|
if (!reqArgs.ticketId)
|
||||||
|
throw new Error('The argument ticketId is required');
|
||||||
|
|
||||||
|
const ticketId = reqArgs.ticketId;
|
||||||
|
const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]);
|
||||||
|
const content = app.toCSV(sales);
|
||||||
|
const fileName = `ticket_${ticketId}.csv`;
|
||||||
|
|
||||||
|
res.setHeader('Content-type', 'application/json; charset=utf-8');
|
||||||
|
res.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
|
||||||
|
res.end(content);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/download', async function(req, res, next) {
|
||||||
|
try {
|
||||||
|
const reqArgs = req.args;
|
||||||
|
if (!reqArgs.ticketId)
|
||||||
|
throw new Error('The argument ticketId is required');
|
||||||
|
|
||||||
|
const ticketId = reqArgs.ticketId;
|
||||||
|
const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]);
|
||||||
|
const content = app.toCSV(sales);
|
||||||
|
const fileName = `ticket_${ticketId}.csv`;
|
||||||
|
|
||||||
|
res.setHeader('Content-type', 'text/csv');
|
||||||
|
res.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
|
||||||
|
res.end(content);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const Email = require('../../../core/email');
|
||||||
|
router.get('/send', async function(req, res, next) {
|
||||||
|
try {
|
||||||
|
const reqArgs = req.args;
|
||||||
|
if (!reqArgs.ticketId)
|
||||||
|
throw new Error('The argument ticketId is required');
|
||||||
|
|
||||||
|
const ticketId = reqArgs.ticketId;
|
||||||
|
const ticket = await db.findOneFromDef(`${sqlPath}/ticket`, [ticketId]);
|
||||||
|
const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [ticketId]);
|
||||||
|
|
||||||
|
const args = Object.assign({
|
||||||
|
ticketId: (String(ticket.id)),
|
||||||
|
recipientId: ticket.clientFk,
|
||||||
|
recipient: ticket.recipient,
|
||||||
|
replyTo: ticket.salesPersonEmail
|
||||||
|
}, reqArgs);
|
||||||
|
|
||||||
|
const content = app.toCSV(sales);
|
||||||
|
const fileName = `ticket_${ticketId}.csv`;
|
||||||
|
const email = new Email('delivery-note', args);
|
||||||
|
await email.send({
|
||||||
|
overrideAttachments: true,
|
||||||
|
attachments: [{
|
||||||
|
filename: fileName,
|
||||||
|
content: content
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({message: 'ok'});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
|
};
|
|
@ -0,0 +1,35 @@
|
||||||
|
SELECT io.ref Invoice,
|
||||||
|
io.issued InvoiceDate,
|
||||||
|
s.ticketFk Ticket,
|
||||||
|
s.itemFk Item,
|
||||||
|
s.concept Description,
|
||||||
|
i.size,
|
||||||
|
i.subName Producer,
|
||||||
|
s.quantity Quantity,
|
||||||
|
s.price Price,
|
||||||
|
s.discount Discount,
|
||||||
|
s.created Created,
|
||||||
|
tc.code Taxcode,
|
||||||
|
tc.description TaxDescription,
|
||||||
|
i.tag5,
|
||||||
|
i.value5,
|
||||||
|
i.tag6,
|
||||||
|
i.value6,
|
||||||
|
i.tag7,
|
||||||
|
i.value7,
|
||||||
|
i.tag8,
|
||||||
|
i.value8,
|
||||||
|
i.tag9,
|
||||||
|
i.value9,
|
||||||
|
i.tag10,
|
||||||
|
i.value10
|
||||||
|
FROM vn.sale s
|
||||||
|
JOIN vn.ticket t ON t.id = s.ticketFk
|
||||||
|
JOIN vn.item i ON i.id = s.itemFk
|
||||||
|
JOIN vn.supplier s2 ON s2.id = t.companyFk
|
||||||
|
JOIN vn.itemTaxCountry itc ON itc.itemFk = i.id
|
||||||
|
AND itc.countryFk = s2.countryFk
|
||||||
|
JOIN vn.taxClass tc ON tc.id = itc.taxClassFk
|
||||||
|
LEFT JOIN vn.invoiceOut io ON io.id = t.refFk
|
||||||
|
WHERE s.ticketFk = ?
|
||||||
|
ORDER BY s.ticketFk, s.created
|
|
@ -0,0 +1,9 @@
|
||||||
|
SELECT
|
||||||
|
t.id,
|
||||||
|
t.clientFk,
|
||||||
|
c.email recipient,
|
||||||
|
eu.email salesPersonEmail
|
||||||
|
FROM ticket t
|
||||||
|
JOIN client c ON c.id = t.clientFk
|
||||||
|
LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
|
||||||
|
WHERE t.id = ?
|
|
@ -0,0 +1,82 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = new express.Router();
|
||||||
|
const path = require('path');
|
||||||
|
const db = require('../../../core/database');
|
||||||
|
const sqlPath = path.join(__dirname, 'sql');
|
||||||
|
|
||||||
|
module.exports = app => {
|
||||||
|
router.get('/preview', async function(req, res, next) {
|
||||||
|
try {
|
||||||
|
const reqArgs = req.args;
|
||||||
|
if (!reqArgs.invoiceId)
|
||||||
|
throw new Error('The argument invoiceId is required');
|
||||||
|
|
||||||
|
const invoiceId = reqArgs.invoiceId;
|
||||||
|
const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]);
|
||||||
|
const content = app.toCSV(sales);
|
||||||
|
const fileName = `invoice_${invoiceId}.csv`;
|
||||||
|
|
||||||
|
res.setHeader('Content-type', 'application/json; charset=utf-8');
|
||||||
|
res.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
|
||||||
|
res.end(content);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/download', async function(req, res, next) {
|
||||||
|
try {
|
||||||
|
const reqArgs = req.args;
|
||||||
|
if (!reqArgs.invoiceId)
|
||||||
|
throw new Error('The argument invoiceId is required');
|
||||||
|
|
||||||
|
const invoiceId = reqArgs.invoiceId;
|
||||||
|
const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]);
|
||||||
|
const content = app.toCSV(sales);
|
||||||
|
const fileName = `invoice_${invoiceId}.csv`;
|
||||||
|
|
||||||
|
res.setHeader('Content-type', 'text/csv');
|
||||||
|
res.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
|
||||||
|
res.end(content);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const Email = require('../../../core/email');
|
||||||
|
router.get('/send', async function(req, res, next) {
|
||||||
|
try {
|
||||||
|
const reqArgs = req.args;
|
||||||
|
if (!reqArgs.invoiceId)
|
||||||
|
throw new Error('The argument invoiceId is required');
|
||||||
|
|
||||||
|
const invoiceId = reqArgs.invoiceId;
|
||||||
|
const invoice = await db.findOneFromDef(`${sqlPath}/invoice`, [invoiceId]);
|
||||||
|
const sales = await db.rawSqlFromDef(`${sqlPath}/sales`, [invoiceId]);
|
||||||
|
|
||||||
|
const args = Object.assign({
|
||||||
|
invoiceId: (String(invoice.id)),
|
||||||
|
recipientId: invoice.clientFk,
|
||||||
|
recipient: invoice.recipient,
|
||||||
|
replyTo: invoice.salesPersonEmail
|
||||||
|
}, reqArgs);
|
||||||
|
|
||||||
|
const content = app.toCSV(sales);
|
||||||
|
const fileName = `invoice_${invoiceId}.csv`;
|
||||||
|
const email = new Email('invoice', args);
|
||||||
|
await email.send({
|
||||||
|
overrideAttachments: true,
|
||||||
|
attachments: [{
|
||||||
|
filename: fileName,
|
||||||
|
content: content
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({message: 'ok'});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
SELECT
|
||||||
|
io.id,
|
||||||
|
io.clientFk,
|
||||||
|
c.email recipient,
|
||||||
|
eu.email salesPersonEmail
|
||||||
|
FROM invoiceOut io
|
||||||
|
JOIN client c ON c.id = io.clientFk
|
||||||
|
LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk
|
||||||
|
WHERE io.id = ?
|
|
@ -0,0 +1,35 @@
|
||||||
|
SELECT io.ref Invoice,
|
||||||
|
io.issued InvoiceDate,
|
||||||
|
s.ticketFk Ticket,
|
||||||
|
s.itemFk Item,
|
||||||
|
s.concept Description,
|
||||||
|
i.size,
|
||||||
|
i.subName Producer,
|
||||||
|
s.quantity Quantity,
|
||||||
|
s.price Price,
|
||||||
|
s.discount Discount,
|
||||||
|
s.created Created,
|
||||||
|
tc.code Taxcode,
|
||||||
|
tc.description TaxDescription,
|
||||||
|
i.tag5,
|
||||||
|
i.value5,
|
||||||
|
i.tag6,
|
||||||
|
i.value6,
|
||||||
|
i.tag7,
|
||||||
|
i.value7,
|
||||||
|
i.tag8,
|
||||||
|
i.value8,
|
||||||
|
i.tag9,
|
||||||
|
i.value9,
|
||||||
|
i.tag10,
|
||||||
|
i.value10
|
||||||
|
FROM sale s
|
||||||
|
JOIN ticket t ON t.id = s.ticketFk
|
||||||
|
JOIN item i ON i.id = s.itemFk
|
||||||
|
JOIN supplier s2 ON s2.id = t.companyFk
|
||||||
|
JOIN itemTaxCountry itc ON itc.itemFk = i.id
|
||||||
|
AND itc.countryFk = s2.countryFk
|
||||||
|
JOIN taxClass tc ON tc.id = itc.taxClassFk
|
||||||
|
JOIN invoiceOut io ON io.ref = t.refFk
|
||||||
|
WHERE io.id = ?
|
||||||
|
ORDER BY s.ticketFk, s.created
|
|
@ -29,6 +29,9 @@ module.exports = {
|
||||||
|
|
||||||
const hash = md5(this.signature.id.toString()).substring(0, 3);
|
const hash = md5(this.signature.id.toString()).substring(0, 3);
|
||||||
const file = `${config.storage.root}/${hash}/${this.signature.id}.png`;
|
const file = `${config.storage.root}/${hash}/${this.signature.id}.png`;
|
||||||
|
|
||||||
|
if (!fs.existsSync(file)) return null;
|
||||||
|
|
||||||
const src = fs.readFileSync(file);
|
const src = fs.readFileSync(file);
|
||||||
const base64 = Buffer.from(src, 'utf8').toString('base64');
|
const base64 = Buffer.from(src, 'utf8').toString('base64');
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,6 @@ module.exports = {
|
||||||
},
|
},
|
||||||
ticketSubtotal(ticket) {
|
ticketSubtotal(ticket) {
|
||||||
let subTotal = 0.00;
|
let subTotal = 0.00;
|
||||||
console.log(ticket.sales);
|
|
||||||
for (let sale of ticket.sales)
|
for (let sale of ticket.sales)
|
||||||
subTotal += this.saleImport(sale);
|
subTotal += this.saleImport(sale);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue