Merge branch 'dev' into 2995-supplierAccount-modificaciones
This commit is contained in:
commit
3365eadf5e
|
@ -6,6 +6,11 @@ ALTER TABLE `vn`.`address` AUTO_INCREMENT = 1;
|
||||||
ALTER TABLE `vn`.`zoneGeo` AUTO_INCREMENT = 1;
|
ALTER TABLE `vn`.`zoneGeo` AUTO_INCREMENT = 1;
|
||||||
ALTER TABLE `vn`.`ticket` AUTO_INCREMENT = 1;
|
ALTER TABLE `vn`.`ticket` AUTO_INCREMENT = 1;
|
||||||
|
|
||||||
|
INSERT INTO `salix`.`AccessToken` (`id`, `ttl`, `created`, `userId`)
|
||||||
|
VALUES
|
||||||
|
('TOTALLY_SECURE_TOKEN', '1209600', CURDATE(), 66);
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO `vn`.`ticketConfig` (`id`, `scopeDays`)
|
INSERT INTO `vn`.`ticketConfig` (`id`, `scopeDays`)
|
||||||
VALUES
|
VALUES
|
||||||
('1', '6');
|
('1', '6');
|
||||||
|
@ -125,14 +130,14 @@ INSERT INTO `vn`.`warehouseAlias`(`id`, `name`)
|
||||||
(1, 'Main Warehouse'),
|
(1, 'Main Warehouse'),
|
||||||
(2, 'Silla');
|
(2, 'Silla');
|
||||||
|
|
||||||
INSERT INTO `vn`.`warehouse`(`id`, `name`, `code`, `isComparative`, `isInventory`, `hasAvailable`, `isManaged`, `hasStowaway`, `hasDms`, `hasComission`, `aliasFk`, `countryFk`)
|
INSERT INTO `vn`.`warehouse`(`id`, `name`, `code`, `isComparative`, `isInventory`, `hasAvailable`, `isManaged`, `hasStowaway`, `hasDms`, `hasComission`, `aliasFk`, `countryFk`, `hasProduction`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'Warehouse One', 'ALG', 1, 1, 1, 1, 1, 1, 1, 2, 1),
|
(1, 'Warehouse One', 'ALG', 1, 1, 1, 1, 1, 1, 1, 2, 1, 1),
|
||||||
(2, 'Warehouse Two', NULL, 1, 1, 1, 1, 0, 0, 1, 2, 13),
|
(2, 'Warehouse Two', NULL, 1, 1, 1, 1, 0, 0, 1, 2, 13, 1),
|
||||||
(3, 'Warehouse Three', NULL, 1, 1, 1, 1, 0, 0, 0, 2, 1),
|
(3, 'Warehouse Three', NULL, 1, 1, 1, 1, 0, 0, 0, 2, 1, 1),
|
||||||
(4, 'Warehouse Four', NULL, 1, 1, 1, 1, 0, 0, 0, 2, 1),
|
(4, 'Warehouse Four', NULL, 1, 1, 1, 1, 0, 0, 0, 2, 1, 1),
|
||||||
(5, 'Warehouse Five', NULL, 1, 1, 1, 1, 0, 0, 0, 2, 1),
|
(5, 'Warehouse Five', NULL, 1, 1, 1, 1, 0, 0, 0, 2, 1, 1),
|
||||||
(13, 'Inventory', NULL, 1, 1, 1, 0, 0, 0, 0, 2, 1);
|
(13, 'Inventory', NULL, 1, 1, 1, 0, 0, 0, 0, 2, 1, 0);
|
||||||
|
|
||||||
INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `isPreviousPreparedByPacking`, `code`, `pickingPlacement`, `path`)
|
INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `isPreviousPreparedByPacking`, `code`, `pickingPlacement`, `path`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -208,7 +213,7 @@ UPDATE `vn`.`agencyMode` SET `deliveryMethodFk` = 1 WHERE `id` = 8;
|
||||||
UPDATE `vn`.`agencyMode` SET `deliveryMethodFk` = 4 WHERE `id` = 23;
|
UPDATE `vn`.`agencyMode` SET `deliveryMethodFk` = 4 WHERE `id` = 23;
|
||||||
UPDATE `vn`.`agencyMode` SET `deliveryMethodFk` = 1 WHERE `id` = 10;
|
UPDATE `vn`.`agencyMode` SET `deliveryMethodFk` = 1 WHERE `id` = 10;
|
||||||
|
|
||||||
UPDATE `vn`.`agencyMode` SET `web` = 1;
|
UPDATE `vn`.`agencyMode` SET `web` = 1, `reportMail` = 'no-reply@gothamcity.com';
|
||||||
|
|
||||||
UPDATE `vn`.`agencyMode` SET `code` = 'refund' WHERE `id` = 23;
|
UPDATE `vn`.`agencyMode` SET `code` = 'refund' WHERE `id` = 23;
|
||||||
|
|
||||||
|
@ -846,7 +851,7 @@ INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `isBox`, `create
|
||||||
(7, 2, 4, 71, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), NULL, 1, 1, 18, NULL, 94),
|
(7, 2, 4, 71, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), NULL, 1, 1, 18, NULL, 94),
|
||||||
(8, 3, 5, 71, DATE_ADD(CURDATE(), INTERVAL -4 MONTH), NULL, 1, 1, 18, NULL, 94),
|
(8, 3, 5, 71, DATE_ADD(CURDATE(), INTERVAL -4 MONTH), NULL, 1, 1, 18, NULL, 94),
|
||||||
(9, 3, 6, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 1, 1, 18, NULL, 94),
|
(9, 3, 6, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 1, 1, 18, NULL, 94),
|
||||||
(10, 7, 7, 71, CURDATE(), NULL, 1, 1, 18, NULL, 94);
|
(10, 7, 7, 71, NOW(), NULL, 1, 1, 18, NULL, 94);
|
||||||
|
|
||||||
INSERT INTO `vn`.`ticketPackaging`(`id`, `ticketFk`, `packagingFk`, `quantity`, `created`, `pvp`)
|
INSERT INTO `vn`.`ticketPackaging`(`id`, `ticketFk`, `packagingFk`, `quantity`, `created`, `pvp`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2410,3 +2415,5 @@ INSERT INTO `vn`.`expeditionScan` (`id`, `expeditionFk`, `scanned`, `palletFk`)
|
||||||
(8, 8, CURDATE(), 1),
|
(8, 8, CURDATE(), 1),
|
||||||
(9, 9, CURDATE(), 1),
|
(9, 9, CURDATE(), 1),
|
||||||
(10, 10, CURDATE(), 1);
|
(10, 10, CURDATE(), 1);
|
||||||
|
|
||||||
|
CALL `cache`.`last_buy_refresh`(FALSE);
|
|
@ -3,22 +3,18 @@ const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
|
|
||||||
describe('last_buy_refresh()', () => {
|
describe('last_buy_refresh()', () => {
|
||||||
it(`should store some data on cache.last_buy`, async() => {
|
it(`should store some data on cache.last_buy`, async() => {
|
||||||
let stmts = [];
|
const stmts = [];
|
||||||
let stmt;
|
|
||||||
|
|
||||||
stmts.push('START TRANSACTION');
|
stmts.push('START TRANSACTION');
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL cache.last_buy_refresh(true)');
|
const lastBuyTableIndex = stmts.push(`SELECT * FROM cache.last_buy ORDER BY item_id ASC`) - 1;
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
let lastBuyTableIndex = stmts.push(`SELECT * FROM cache.last_buy ORDER BY item_id ASC`) - 1;
|
|
||||||
|
|
||||||
stmts.push('ROLLBACK');
|
stmts.push('ROLLBACK');
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
const sql = ParameterizedSQL.join(stmts, ';');
|
||||||
let result = await app.models.Ticket.rawStmt(sql);
|
const result = await app.models.Ticket.rawStmt(sql);
|
||||||
|
|
||||||
let lastBuyTable = result[lastBuyTableIndex];
|
const lastBuyTable = result[lastBuyTableIndex];
|
||||||
|
|
||||||
expect(lastBuyTable.length).toEqual(12);
|
expect(lastBuyTable.length).toEqual(12);
|
||||||
|
|
||||||
|
|
|
@ -186,9 +186,10 @@ export default {
|
||||||
receivedB2BVNLCheckbox: 'vn-client-billing-data vn-check[label="Received B2B VNL"]',
|
receivedB2BVNLCheckbox: 'vn-client-billing-data vn-check[label="Received B2B VNL"]',
|
||||||
swiftBic: 'vn-client-billing-data vn-autocomplete[ng-model="$ctrl.client.bankEntityFk"]',
|
swiftBic: 'vn-client-billing-data vn-autocomplete[ng-model="$ctrl.client.bankEntityFk"]',
|
||||||
newBankEntityButton: 'vn-client-billing-data vn-icon-button[vn-tooltip="New bank entity"] > button',
|
newBankEntityButton: 'vn-client-billing-data vn-icon-button[vn-tooltip="New bank entity"] > button',
|
||||||
newBankEntityName: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.newBankEntity.name"]',
|
newBankEntityName: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.data.name"]',
|
||||||
newBankEntityBIC: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.newBankEntity.bic"]',
|
newBankEntityBIC: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.data.bic"]',
|
||||||
newBankEntityCode: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.newBankEntity.id"]',
|
newBankEntityCountry: '.vn-dialog.shown vn-autocomplete[ng-model="$ctrl.data.countryFk"]',
|
||||||
|
newBankEntityCode: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.data.id"]',
|
||||||
acceptBankEntityButton: '.vn-dialog.shown button[response="accept"]',
|
acceptBankEntityButton: '.vn-dialog.shown button[response="accept"]',
|
||||||
saveButton: 'vn-client-billing-data button[type=submit]',
|
saveButton: 'vn-client-billing-data button[type=submit]',
|
||||||
watcher: 'vn-client-billing-data vn-watcher'
|
watcher: 'vn-client-billing-data vn-watcher'
|
||||||
|
|
|
@ -34,8 +34,9 @@ describe('Client Edit billing data path', () => {
|
||||||
it(`should create a new BIC code`, async() => {
|
it(`should create a new BIC code`, async() => {
|
||||||
await page.waitToClick(selectors.clientBillingData.newBankEntityButton);
|
await page.waitToClick(selectors.clientBillingData.newBankEntityButton);
|
||||||
await page.write(selectors.clientBillingData.newBankEntityName, 'Gotham City Bank');
|
await page.write(selectors.clientBillingData.newBankEntityName, 'Gotham City Bank');
|
||||||
await page.write(selectors.clientBillingData.newBankEntityCode, '9999');
|
|
||||||
await page.write(selectors.clientBillingData.newBankEntityBIC, 'GTHMCT');
|
await page.write(selectors.clientBillingData.newBankEntityBIC, 'GTHMCT');
|
||||||
|
await page.autocompleteSearch(selectors.clientBillingData.newBankEntityCountry, 'España');
|
||||||
|
await page.write(selectors.clientBillingData.newBankEntityCode, '9999');
|
||||||
await page.waitToClick(selectors.clientBillingData.acceptBankEntityButton);
|
await page.waitToClick(selectors.clientBillingData.acceptBankEntityButton);
|
||||||
await page.waitForTextInField(selectors.clientBillingData.swiftBic, 'Gotham City Bank');
|
await page.waitForTextInField(selectors.clientBillingData.swiftBic, 'Gotham City Bank');
|
||||||
const newcode = await page.waitToGetProperty(selectors.clientBillingData.swiftBic, 'value');
|
const newcode = await page.waitToGetProperty(selectors.clientBillingData.swiftBic, 'value');
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
ng-if="$ctrl.displayControls">
|
ng-if="$ctrl.displayControls">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="days-header" ng-class="{'hide-weeks': $ctrl.hideWeeks}">
|
||||||
|
<div class="week-numbers" ng-if="!$ctrl.hideWeeks"></div>
|
||||||
<div class="weekdays">
|
<div class="weekdays">
|
||||||
<section
|
<section
|
||||||
ng-repeat="day in ::$ctrl.weekDays"
|
ng-repeat="day in ::$ctrl.weekDays"
|
||||||
|
@ -27,6 +29,17 @@
|
||||||
<span>{{::day.localeChar}}</span>
|
<span>{{::day.localeChar}}</span>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="days-container" ng-class="{'hide-weeks': $ctrl.hideWeeks}">
|
||||||
|
<div class="weeks" ng-if="!$ctrl.hideWeeks">
|
||||||
|
<section ng-repeat="week in $ctrl.weekNumbers"
|
||||||
|
class="day">
|
||||||
|
<div class="day-number">
|
||||||
|
{{::week}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
class="days"
|
class="days"
|
||||||
ng-class="{'hide-contiguous': $ctrl.hideContiguous}">
|
ng-class="{'hide-contiguous': $ctrl.hideContiguous}">
|
||||||
|
@ -43,4 +56,5 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -12,11 +12,12 @@ import './style.scss';
|
||||||
* @event move Emitted when month changes
|
* @event move Emitted when month changes
|
||||||
*/
|
*/
|
||||||
export default class Calendar extends FormInput {
|
export default class Calendar extends FormInput {
|
||||||
constructor($element, $scope, vnWeekDays) {
|
constructor($element, $scope, vnWeekDays, moment) {
|
||||||
super($element, $scope);
|
super($element, $scope);
|
||||||
this.weekDays = vnWeekDays.locales;
|
this.weekDays = vnWeekDays.locales;
|
||||||
this.defaultDate = new Date();
|
this.defaultDate = new Date();
|
||||||
this.displayControls = true;
|
this.displayControls = true;
|
||||||
|
this.moment = moment;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,15 +55,23 @@ export default class Calendar extends FormInput {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastDay() {
|
||||||
|
return new Date(
|
||||||
|
this.defaultDate.getFullYear(),
|
||||||
|
this.defaultDate.getMonth() + 1,
|
||||||
|
0
|
||||||
|
).getDate();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repaints the calendar.
|
* Repaints the calendar.
|
||||||
*/
|
*/
|
||||||
repaint() {
|
repaint() {
|
||||||
const firstWeekday = this.firstDay(this.defaultDate).getDay() - 1;
|
const firstWeekday = this.firstDay(this.defaultDate).getDay() - 1;
|
||||||
let weekdayOffset = firstWeekday >= 0 ? firstWeekday : 6;
|
this.weekdayOffset = firstWeekday >= 0 ? firstWeekday : 6;
|
||||||
|
|
||||||
let dayIndex = new Date(this.defaultDate.getTime());
|
let dayIndex = new Date(this.defaultDate.getTime());
|
||||||
dayIndex.setDate(1 - weekdayOffset);
|
dayIndex.setDate(1 - this.weekdayOffset);
|
||||||
|
|
||||||
this.days = [];
|
this.days = [];
|
||||||
|
|
||||||
|
@ -70,27 +79,55 @@ export default class Calendar extends FormInput {
|
||||||
this.days.push(new Date(dayIndex.getTime()));
|
this.days.push(new Date(dayIndex.getTime()));
|
||||||
dayIndex.setDate(dayIndex.getDate() + 1);
|
dayIndex.setDate(dayIndex.getDate() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.getWeekdays();
|
||||||
|
}
|
||||||
|
|
||||||
|
getWeekdays() {
|
||||||
|
if (!this.moment) return;
|
||||||
|
|
||||||
|
const totalSlots = this.lastDay() + this.weekdayOffset;
|
||||||
|
const weeks = Math.ceil(totalSlots / 7);
|
||||||
|
|
||||||
|
const dated = this.moment(this.defaultDate);
|
||||||
|
const firstWeekNumber = dated.set('date', 1).isoWeek();
|
||||||
|
|
||||||
|
const weekNumbers = [];
|
||||||
|
for (let w = 0; w < weeks; w++) {
|
||||||
|
let weekNumber = firstWeekNumber;
|
||||||
|
if (dated.get('month') == 0 && firstWeekNumber > 1 && w > 0)
|
||||||
|
weekNumber = 0;
|
||||||
|
|
||||||
|
weekNumbers.push(weekNumber + w);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.weekNumbers = weekNumbers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets CSS classes to apply to the specified day.
|
* Gets CSS classes to apply to the specified day.
|
||||||
*
|
*
|
||||||
* @param {Date} day The day
|
* @param {Date} date The date
|
||||||
* @return {Object} The CSS classes to apply
|
* @return {Object} The CSS classes to apply
|
||||||
*/
|
*/
|
||||||
getDayClasses(day) {
|
getDayClasses(date) {
|
||||||
let wday = day.getDay();
|
let day = date.getDate();
|
||||||
let month = day.getMonth();
|
let wday = date.getDay();
|
||||||
|
let month = date.getMonth();
|
||||||
|
|
||||||
|
const currentDay = new Date().getDate();
|
||||||
|
const currentMonth = new Date().getMonth();
|
||||||
|
|
||||||
let classes = {
|
let classes = {
|
||||||
|
today: day === currentDay && month === currentMonth,
|
||||||
weekend: wday === 6 || wday === 0,
|
weekend: wday === 6 || wday === 0,
|
||||||
previous: month < this.month,
|
previous: month < this.month,
|
||||||
current: month == this.month,
|
current: month == this.month,
|
||||||
next: month > this.month,
|
next: month > this.month,
|
||||||
event: this.hasEvents({$day: day})
|
event: this.hasEvents({$day: date})
|
||||||
};
|
};
|
||||||
|
|
||||||
let userClass = this.getClass({$day: day});
|
let userClass = this.getClass({$day: date});
|
||||||
if (userClass) classes[userClass] = true;
|
if (userClass) classes[userClass] = true;
|
||||||
|
|
||||||
return classes;
|
return classes;
|
||||||
|
@ -181,7 +218,7 @@ export default class Calendar extends FormInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Calendar.$inject = ['$element', '$scope', 'vnWeekDays'];
|
Calendar.$inject = ['$element', '$scope', 'vnWeekDays', 'moment'];
|
||||||
|
|
||||||
ngModule.vnComponent('vnCalendar', {
|
ngModule.vnComponent('vnCalendar', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
@ -193,6 +230,7 @@ ngModule.vnComponent('vnCalendar', {
|
||||||
formatDay: '&?',
|
formatDay: '&?',
|
||||||
displayControls: '<?',
|
displayControls: '<?',
|
||||||
hideYear: '<?',
|
hideYear: '<?',
|
||||||
hideContiguous: '<?'
|
hideContiguous: '<?',
|
||||||
|
hideWeeks: '<?'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,7 +19,14 @@
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
& > .weekdays {
|
& #days-header {
|
||||||
|
flex-direction: row;
|
||||||
|
display: flex
|
||||||
|
}
|
||||||
|
& #days-header > .week-numbers {
|
||||||
|
width: 10%
|
||||||
|
}
|
||||||
|
& #days-header > .weekdays {
|
||||||
display: flex;
|
display: flex;
|
||||||
color: $color-font-secondary;
|
color: $color-font-secondary;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
@ -27,17 +34,49 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
width: 90%;
|
||||||
|
|
||||||
& > section {
|
& > section {
|
||||||
width: 14.28%;
|
width: 14.28%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
& > .days {
|
& #days-header.hide-weeks {
|
||||||
|
& > .weekdays {
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& > #days-container {
|
||||||
|
flex-direction: row;
|
||||||
|
display: flex
|
||||||
|
}
|
||||||
|
& > #days-container > .weeks {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
color: $color-font-secondary;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: .8rem;
|
||||||
|
width: 10%;
|
||||||
|
|
||||||
|
& > .day {
|
||||||
|
height: 40px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& #days-container.hide-weeks {
|
||||||
|
& > .days {
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#days-container > .days {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
width: 90%;
|
||||||
|
|
||||||
& > .day {
|
& > .day {
|
||||||
width: 14.28%;
|
width: 14.28%;
|
||||||
|
@ -46,6 +85,17 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
&.today {
|
||||||
|
color: $color-font-bg;
|
||||||
|
& > .day-number {
|
||||||
|
border: 2px solid $color-font-link;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: lighten($color-font-link, 20%);
|
||||||
|
opacity: .8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
&.weekend {
|
&.weekend {
|
||||||
color: $color-font-secondary;
|
color: $color-font-secondary;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,15 @@ import 'angular-translate-loader-partial';
|
||||||
import '@uirouter/angularjs';
|
import '@uirouter/angularjs';
|
||||||
import 'mg-crud';
|
import 'mg-crud';
|
||||||
import 'oclazyload';
|
import 'oclazyload';
|
||||||
|
import 'angular-moment';
|
||||||
|
|
||||||
export const ngDeps = [
|
export const ngDeps = [
|
||||||
'ngAnimate',
|
'ngAnimate',
|
||||||
'pascalprecht.translate',
|
'pascalprecht.translate',
|
||||||
'ui.router',
|
'ui.router',
|
||||||
'mgCrud',
|
'mgCrud',
|
||||||
'oc.lazyLoad'
|
'oc.lazyLoad',
|
||||||
|
'angularMoment'
|
||||||
];
|
];
|
||||||
|
|
||||||
import * as validator from 'validator';
|
import * as validator from 'validator';
|
||||||
|
|
|
@ -3,6 +3,181 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "salix-front",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "GPL-3.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@uirouter/angularjs": "^1.0.20",
|
||||||
|
"angular": "^1.7.5",
|
||||||
|
"angular-animate": "^1.7.8",
|
||||||
|
"angular-moment": "^1.3.0",
|
||||||
|
"angular-translate": "^2.18.1",
|
||||||
|
"angular-translate-loader-partial": "^2.18.1",
|
||||||
|
"croppie": "^2.6.5",
|
||||||
|
"js-yaml": "^3.13.1",
|
||||||
|
"mg-crud": "^1.1.2",
|
||||||
|
"oclazyload": "^0.6.3",
|
||||||
|
"require-yaml": "0.0.1",
|
||||||
|
"validator": "^6.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@uirouter/angularjs": {
|
||||||
|
"version": "1.0.29",
|
||||||
|
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.29.tgz",
|
||||||
|
"integrity": "sha512-RImWnBarNixkMto0o8stEaGwZmvhv5cnuOLXyMU2pY8MP2rgEF74ZNJTLeJCW14LR7XDUxVH8Mk8bPI6lxedmQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@uirouter/core": "6.0.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@uirouter/core": {
|
||||||
|
"version": "6.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.7.tgz",
|
||||||
|
"integrity": "sha512-KUTJxL+6q0PiBnFx4/Z+Hsyg0pSGiaW5yZQeJmUxknecjpTbnXkLU8H2EqRn9N2B+qDRa7Jg8RcgeNDPY72O1w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/angular": {
|
||||||
|
"version": "1.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.2.tgz",
|
||||||
|
"integrity": "sha512-IauMOej2xEe7/7Ennahkbb5qd/HFADiNuLSESz9Q27inmi32zB0lnAsFeLEWcox3Gd1F6YhNd1CP7/9IukJ0Gw=="
|
||||||
|
},
|
||||||
|
"node_modules/angular-animate": {
|
||||||
|
"version": "1.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.2.tgz",
|
||||||
|
"integrity": "sha512-Jbr9+grNMs9Kj57xuBU3Ju3NOPAjS1+g2UAwwDv7su1lt0/PLDy+9zEwDiu8C8xJceoTbmBNKiWGPJGBdCQLlA=="
|
||||||
|
},
|
||||||
|
"node_modules/angular-moment": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/angular-moment/-/angular-moment-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-KG8rvO9MoaBLwtGnxTeUveSyNtrL+RNgGl1zqWN36+HDCCVGk2DGWOzqKWB6o+eTTbO3Opn4hupWKIElc8XETA==",
|
||||||
|
"dependencies": {
|
||||||
|
"moment": ">=2.8.0 <3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/angular-translate": {
|
||||||
|
"version": "2.18.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.4.tgz",
|
||||||
|
"integrity": "sha512-KohNrkH6J9PK+VW0L/nsRTcg5Fw70Ajwwe3Jbfm54Pf9u9Fd+wuingoKv+h45mKf38eT+Ouu51FPua8VmZNoCw==",
|
||||||
|
"dependencies": {
|
||||||
|
"angular": "^1.8.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/angular-translate-loader-partial": {
|
||||||
|
"version": "2.18.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.18.4.tgz",
|
||||||
|
"integrity": "sha512-bsjR+FbB0sdA2528E/ugwKdlPPQhA1looxLxI3otayBTFXBpED33besfSZhYAISLgNMSL038vSssfRUen9qD8w==",
|
||||||
|
"dependencies": {
|
||||||
|
"angular-translate": "~2.18.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/argparse": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
|
"dependencies": {
|
||||||
|
"sprintf-js": "~1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/croppie": {
|
||||||
|
"version": "2.6.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/croppie/-/croppie-2.6.5.tgz",
|
||||||
|
"integrity": "sha512-IlChnVUGG5T3w2gRZIaQgBtlvyuYnlUWs2YZIXXR3H9KrlO1PtBT3j+ykxvy9eZIWhk+V5SpBmhCQz5UXKrEKQ=="
|
||||||
|
},
|
||||||
|
"node_modules/esprima": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||||
|
"bin": {
|
||||||
|
"esparse": "bin/esparse.js",
|
||||||
|
"esvalidate": "bin/esvalidate.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/js-yaml": {
|
||||||
|
"version": "3.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||||
|
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": "^1.0.7",
|
||||||
|
"esprima": "^4.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"js-yaml": "bin/js-yaml.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mg-crud": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/mg-crud/-/mg-crud-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-p6AWGzWSPK7/8ZpIBpS2V1vDggw=",
|
||||||
|
"dependencies": {
|
||||||
|
"angular": "^1.6.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/moment": {
|
||||||
|
"version": "2.29.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||||
|
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/oclazyload": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/oclazyload/-/oclazyload-0.6.3.tgz",
|
||||||
|
"integrity": "sha1-Kjirv/QJDAihEBZxkZRbWfLoJ5w="
|
||||||
|
},
|
||||||
|
"node_modules/require-yaml": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-yaml/-/require-yaml-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-LhsY2RPDuqcqWk03O28Tjd0sMr0=",
|
||||||
|
"dependencies": {
|
||||||
|
"js-yaml": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/require-yaml/node_modules/argparse": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||||
|
},
|
||||||
|
"node_modules/require-yaml/node_modules/js-yaml": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": "^2.0.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"js-yaml": "bin/js-yaml.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/sprintf-js": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||||
|
},
|
||||||
|
"node_modules/validator": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz",
|
||||||
|
"integrity": "sha1-R84j7Y1Ord+p1LjvAHG2zxB418g=",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@uirouter/angularjs": {
|
"@uirouter/angularjs": {
|
||||||
"version": "1.0.29",
|
"version": "1.0.29",
|
||||||
|
@ -27,6 +202,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.2.tgz",
|
||||||
"integrity": "sha512-Jbr9+grNMs9Kj57xuBU3Ju3NOPAjS1+g2UAwwDv7su1lt0/PLDy+9zEwDiu8C8xJceoTbmBNKiWGPJGBdCQLlA=="
|
"integrity": "sha512-Jbr9+grNMs9Kj57xuBU3Ju3NOPAjS1+g2UAwwDv7su1lt0/PLDy+9zEwDiu8C8xJceoTbmBNKiWGPJGBdCQLlA=="
|
||||||
},
|
},
|
||||||
|
"angular-moment": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/angular-moment/-/angular-moment-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-KG8rvO9MoaBLwtGnxTeUveSyNtrL+RNgGl1zqWN36+HDCCVGk2DGWOzqKWB6o+eTTbO3Opn4hupWKIElc8XETA==",
|
||||||
|
"requires": {
|
||||||
|
"moment": ">=2.8.0 <3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"angular-translate": {
|
"angular-translate": {
|
||||||
"version": "2.18.4",
|
"version": "2.18.4",
|
||||||
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.4.tgz",
|
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.4.tgz",
|
||||||
|
@ -78,6 +261,11 @@
|
||||||
"angular": "^1.6.1"
|
"angular": "^1.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.29.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
|
||||||
|
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
||||||
|
},
|
||||||
"oclazyload": {
|
"oclazyload": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/oclazyload/-/oclazyload-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/oclazyload/-/oclazyload-0.6.3.tgz",
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"@uirouter/angularjs": "^1.0.20",
|
"@uirouter/angularjs": "^1.0.20",
|
||||||
"angular": "^1.7.5",
|
"angular": "^1.7.5",
|
||||||
"angular-animate": "^1.7.8",
|
"angular-animate": "^1.7.8",
|
||||||
|
"angular-moment": "^1.3.0",
|
||||||
"angular-translate": "^2.18.1",
|
"angular-translate": "^2.18.1",
|
||||||
"angular-translate-loader-partial": "^2.18.1",
|
"angular-translate-loader-partial": "^2.18.1",
|
||||||
"croppie": "^2.6.5",
|
"croppie": "^2.6.5",
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
<vn-dialog class="edit"
|
<tpl-title translate>
|
||||||
vn-id="bankEntityDialog"
|
New bank entity
|
||||||
on-open="$ctrl.resetData()"
|
</tpl-title>
|
||||||
on-accept="$ctrl.onAccept()"
|
<tpl-body>
|
||||||
message="New bank entity">
|
|
||||||
<tpl-body>
|
|
||||||
<p translate>Please, ensure you put the correct data!</p>
|
<p translate>Please, ensure you put the correct data!</p>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
|
@ -14,8 +12,6 @@
|
||||||
ng-model="$ctrl.data.name"
|
ng-model="$ctrl.data.name"
|
||||||
required="true">
|
required="true">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
vn-one
|
vn-one
|
||||||
vn-focus
|
vn-focus
|
||||||
|
@ -24,17 +20,27 @@
|
||||||
ng-model="$ctrl.data.bic"
|
ng-model="$ctrl.data.bic"
|
||||||
required="true">
|
required="true">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-autocomplete vn-one
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-autocomplete
|
||||||
|
vn-one
|
||||||
|
vn-id="country"
|
||||||
ng-model="$ctrl.data.countryFk"
|
ng-model="$ctrl.data.countryFk"
|
||||||
url="Countries"
|
url="Countries"
|
||||||
|
fields="['id', 'country', 'code']"
|
||||||
show-field="country"
|
show-field="country"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
label="Country">
|
label="Country">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
|
<vn-textfield
|
||||||
|
vn-one
|
||||||
|
ng-show="country.selection.code === 'ES'"
|
||||||
|
label="Entity code"
|
||||||
|
ng-model="$ctrl.data.id">
|
||||||
|
</vn-textfield>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</tpl-body>
|
</tpl-body>
|
||||||
<tpl-buttons>
|
<tpl-buttons>
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
<button id= "saveBankEntity" response="accept" translate>Save</button>
|
<button response="accept" translate>Accept</button>
|
||||||
</tpl-buttons>
|
</tpl-buttons>
|
||||||
</vn-dialog>
|
|
|
@ -1,35 +1,24 @@
|
||||||
import ngModule from '../../module';
|
import ngModule from '../../module';
|
||||||
import Component from 'core/lib/component';
|
import Dialog from 'core/components/dialog';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
class Controller extends Component {
|
class Controller extends Dialog {
|
||||||
open() {
|
responseHandler(response) {
|
||||||
this.$.bankEntityDialog.show();
|
if (response !== 'accept')
|
||||||
}
|
return super.responseHandler(response);
|
||||||
|
|
||||||
resetData() {
|
|
||||||
this.data = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccept() {
|
|
||||||
try {
|
|
||||||
if (!this.data.countryFk)
|
if (!this.data.countryFk)
|
||||||
throw new Error(`The country can't be empty`);
|
throw new Error(`The country can't be empty`);
|
||||||
|
|
||||||
this.$http.post(`bankEntities`, this.data).then(res => {
|
return this.$http.post(`bankEntities`, this.data)
|
||||||
this.vnApp.showMessage(this.$t('The bank entity has been created. You can save the data now'));
|
.then(res => this.data.id = res.data.id)
|
||||||
this.emit('response', {$response: res.data});
|
.then(() => super.responseHandler(response))
|
||||||
});
|
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
|
||||||
} catch (e) {
|
|
||||||
this.vnApp.showError(this.$t(e.message));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnNewBankEntity', {
|
ngModule.vnComponent('vnNewBankEntity', {
|
||||||
template: require('./index.html'),
|
slotTemplate: require('./index.html'),
|
||||||
controller: Controller,
|
controller: Controller,
|
||||||
bindings: {
|
bindings: {
|
||||||
data: '<',
|
data: '<',
|
||||||
|
|
|
@ -5,49 +5,35 @@ describe('Salix Component vnNewBankEntity', () => {
|
||||||
let $httpBackend;
|
let $httpBackend;
|
||||||
let $scope;
|
let $scope;
|
||||||
let $element;
|
let $element;
|
||||||
let vnApp;
|
|
||||||
|
|
||||||
beforeEach(ngModule('salix'));
|
beforeEach(ngModule('salix'));
|
||||||
|
|
||||||
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _vnApp_) => {
|
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
vnApp = _vnApp_;
|
|
||||||
jest.spyOn(vnApp, 'showError');
|
|
||||||
$scope = $rootScope.$new();
|
$scope = $rootScope.$new();
|
||||||
$element = angular.element('<vn-dialog></dialog>');
|
$element = angular.element('<vn-dialog></vn-dialog>');
|
||||||
controller = $componentController('vnNewBankEntity', {$element, $scope});
|
const $transclude = {
|
||||||
|
$$boundTransclude: {
|
||||||
|
$$slots: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
controller = $componentController('vnNewBankEntity', {$element, $scope, $transclude});
|
||||||
|
controller.vnApp = {showSuccess: jest.fn()};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('resetData()', () => {
|
describe('responseHandler()', () => {
|
||||||
it('should reset the location in the controller', () => {
|
it('should show a success message after the query to bankEntities', () => {
|
||||||
expect(controller.data).toBeUndefined();
|
|
||||||
|
|
||||||
controller.resetData();
|
|
||||||
|
|
||||||
expect(controller.data).toEqual({});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('onAccept()', () => {
|
|
||||||
it('should throw an error if there is no country id in the location', () => {
|
|
||||||
jest.spyOn(controller.vnApp, 'showMessage');
|
|
||||||
|
|
||||||
controller.data = {};
|
|
||||||
|
|
||||||
controller.onAccept();
|
|
||||||
|
|
||||||
expect(controller.vnApp.showError).toHaveBeenCalledWith(`The country can't be empty`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should do add the new bank entity', () => {
|
|
||||||
controller.data = {
|
controller.data = {
|
||||||
countryFk: 1
|
countryFk: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
$httpBackend.expectPOST('bankEntities', controller.data).respond(200, controller.data);
|
$httpBackend.expectPOST('bankEntities', controller.data).respond({id: 1});
|
||||||
|
|
||||||
controller.onAccept();
|
controller.responseHandler('accept');
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
|
||||||
|
expect(controller.data.id).toEqual(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,3 +10,4 @@ The postcode has been created. You can save the data now: Se ha creado el códig
|
||||||
The city has been created: Se ha creado la ciudad
|
The city has been created: Se ha creado la ciudad
|
||||||
The province has been created: Se ha creado la provincia
|
The province has been created: Se ha creado la provincia
|
||||||
The bank entity has been created. You can save the data now: Se ha creado la entidad bancaria. Puedes guardar los datos ahora
|
The bank entity has been created. You can save the data now: Se ha creado la entidad bancaria. Puedes guardar los datos ahora
|
||||||
|
Entity code: Código de la entidad
|
|
@ -4,7 +4,7 @@
|
||||||
message="Edit photo">
|
message="Edit photo">
|
||||||
<tpl-body class="upload-photo">
|
<tpl-body class="upload-photo">
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-one ng-if="file.value">
|
<vn-one ng-if="file.value || $ctrl.newPhoto.url">
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-icon-button vn-none
|
<vn-icon-button vn-none
|
||||||
icon="rotate_left"
|
icon="rotate_left"
|
||||||
|
@ -20,12 +20,26 @@
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-one>
|
<vn-one>
|
||||||
<vn-horizontal>
|
<vn-vertical class="vn-mb-sm">
|
||||||
|
<vn-radio
|
||||||
|
label="Select from computer"
|
||||||
|
val="computer"
|
||||||
|
ng-model="$ctrl.uploadMethod"
|
||||||
|
tabindex="-1">
|
||||||
|
</vn-radio>
|
||||||
|
<vn-radio
|
||||||
|
label="Import from external URL"
|
||||||
|
val="URL"
|
||||||
|
ng-model="$ctrl.uploadMethod"
|
||||||
|
tabindex="-1">
|
||||||
|
</vn-radio>
|
||||||
|
</vn-vertical>
|
||||||
|
<vn-horizontal ng-if="$ctrl.uploadMethod == 'computer'">
|
||||||
<vn-input-file vn-id="file"
|
<vn-input-file vn-id="file"
|
||||||
vn-one
|
vn-one
|
||||||
label="File"
|
label="File"
|
||||||
ng-model="$ctrl.newPhoto.files"
|
ng-model="$ctrl.newPhoto.files"
|
||||||
on-change="$ctrl.updatePhotoPreview(value)"
|
on-change="$ctrl.updatePhotoPreview(value[0])"
|
||||||
accept="{{$ctrl.allowedContentTypes}}"
|
accept="{{$ctrl.allowedContentTypes}}"
|
||||||
required="true">
|
required="true">
|
||||||
<append>
|
<append>
|
||||||
|
@ -37,6 +51,14 @@
|
||||||
</append>
|
</append>
|
||||||
</vn-input-file>
|
</vn-input-file>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
<vn-horizontal ng-if="$ctrl.uploadMethod == 'URL'">
|
||||||
|
<vn-textfield
|
||||||
|
vn-one
|
||||||
|
ng-model="$ctrl.newPhoto.url"
|
||||||
|
on-change="$ctrl.updatePhotoPreview(value)"
|
||||||
|
placeholder="https://">
|
||||||
|
</vn-textfield>
|
||||||
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
label="Type"
|
label="Type"
|
||||||
|
|
|
@ -39,6 +39,7 @@ export default class UploadPhoto extends Component {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
this.viewportType = 'normal';
|
this.viewportType = 'normal';
|
||||||
|
this.uploadMethod = 'computer';
|
||||||
this.getAllowedContentTypes();
|
this.getAllowedContentTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,11 +65,16 @@ export default class UploadPhoto extends Component {
|
||||||
set viewportSelection(value) {
|
set viewportSelection(value) {
|
||||||
this._viewportSelection = value;
|
this._viewportSelection = value;
|
||||||
|
|
||||||
if (value && this.newPhoto.files) {
|
const hasFile = this.newPhoto.files || this.newPhoto.url;
|
||||||
this.displayEditor();
|
if (!value || !hasFile) return;
|
||||||
const files = this.newPhoto.files;
|
|
||||||
this.updatePhotoPreview(files);
|
let file;
|
||||||
}
|
if (this.uploadMethod == 'computer')
|
||||||
|
file = this.newPhoto.files[0];
|
||||||
|
else if (this.uploadMethod == 'URL')
|
||||||
|
file = this.newPhoto.url;
|
||||||
|
|
||||||
|
this.updatePhotoPreview(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllowedContentTypes() {
|
getAllowedContentTypes() {
|
||||||
|
@ -90,13 +96,15 @@ export default class UploadPhoto extends Component {
|
||||||
* @param {string} value
|
* @param {string} value
|
||||||
*/
|
*/
|
||||||
updatePhotoPreview(value) {
|
updatePhotoPreview(value) {
|
||||||
if (value && value[0]) {
|
if (value) {
|
||||||
if (!this.editor)
|
|
||||||
this.displayEditor();
|
this.displayEditor();
|
||||||
|
|
||||||
|
if (this.uploadMethod == 'computer') {
|
||||||
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[0]);
|
reader.readAsDataURL(value);
|
||||||
|
} else if (this.uploadMethod == 'URL')
|
||||||
|
this.editor.bind({url: value});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,17 +24,30 @@ describe('Salix', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('viewportSelection()', () => {
|
describe('viewportSelection()', () => {
|
||||||
it('should call to displayEditor() and updatePhotoPreview() methods', () => {
|
it('should call to the updatePhotoPreview() method when uploadMethod property is set to "computer"', () => {
|
||||||
controller.displayEditor = jest.fn();
|
|
||||||
controller.updatePhotoPreview = jest.fn();
|
controller.updatePhotoPreview = jest.fn();
|
||||||
|
|
||||||
const files = [{name: 'test.jpg'}];
|
const files = [{name: 'test.jpg'}];
|
||||||
controller.newPhoto.files = files;
|
controller.newPhoto.files = files;
|
||||||
|
|
||||||
|
controller.uploadMethod = 'computer';
|
||||||
controller.viewportSelection = {code: 'normal'};
|
controller.viewportSelection = {code: 'normal'};
|
||||||
|
|
||||||
expect(controller.displayEditor).toHaveBeenCalledWith();
|
const firstFile = files[0];
|
||||||
expect(controller.updatePhotoPreview).toHaveBeenCalledWith(files);
|
|
||||||
|
expect(controller.updatePhotoPreview).toHaveBeenCalledWith(firstFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call to the updatePhotoPreview() method when uploadMethod property is set to "URL"', () => {
|
||||||
|
controller.updatePhotoPreview = jest.fn();
|
||||||
|
|
||||||
|
const url = 'http://gothamcity.com/batman.png';
|
||||||
|
controller.newPhoto.url = url;
|
||||||
|
|
||||||
|
controller.uploadMethod = 'URL';
|
||||||
|
controller.viewportSelection = {code: 'normal'};
|
||||||
|
|
||||||
|
expect(controller.updatePhotoPreview).toHaveBeenCalledWith(url);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,3 +4,5 @@ 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ámico
|
||||||
|
Select from computer: Seleccionar desde ordenador
|
||||||
|
Import from external URL: Importar desde URL externa
|
|
@ -206,5 +206,8 @@
|
||||||
"A ticket with an amount of zero can't be invoiced": "No se puede facturar un ticket con importe cero",
|
"A ticket with an amount of zero can't be invoiced": "No se puede facturar un ticket con importe cero",
|
||||||
"A ticket with a negative base can't be invoiced": "No se puede facturar un ticket con una base negativa",
|
"A ticket with a negative base can't be invoiced": "No se puede facturar un ticket con una base negativa",
|
||||||
"Global invoicing failed": "[Facturación global] No se han podido facturar algunos clientes",
|
"Global invoicing failed": "[Facturación global] No se han podido facturar algunos clientes",
|
||||||
"Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes"
|
"Wasn't able to invoice the following clients": "No se han podido facturar los siguientes clientes",
|
||||||
|
"Can't verify data unless the client has a business type": "No se puede verificar datos de un cliente que no tiene tipo de negocio",
|
||||||
|
"You don't have enough privileges to set this credit amount": "No tienes suficientes privilegios para establecer esta cantidad de crédito",
|
||||||
|
"You can't change the credit set to zero from a manager": "No puedes cambiar el cŕedito establecido a cero por un gerente"
|
||||||
}
|
}
|
|
@ -217,19 +217,9 @@ module.exports = Self => {
|
||||||
if (isTaxDataCheckedChanged && !orgData.businessTypeFk)
|
if (isTaxDataCheckedChanged && !orgData.businessTypeFk)
|
||||||
throw new UserError(`Can't verify data unless the client has a business type`);
|
throw new UserError(`Can't verify data unless the client has a business type`);
|
||||||
}
|
}
|
||||||
|
// Credit changes
|
||||||
if (changes.credit !== undefined) {
|
if (changes.credit !== undefined)
|
||||||
await validateCreditChange(ctx, finalState);
|
await Self.changeCredit(ctx, finalState, changes);
|
||||||
let filter = {fields: ['id'], where: {userFk: ctx.options.accessToken.userId}};
|
|
||||||
let worker = await Self.app.models.Worker.findOne(filter);
|
|
||||||
|
|
||||||
let newCredit = {
|
|
||||||
amount: changes.credit,
|
|
||||||
clientFk: finalState.id,
|
|
||||||
workerFk: worker ? worker.id : null
|
|
||||||
};
|
|
||||||
await Self.app.models.ClientCredit.create(newCredit);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.observe('after save', async ctx => {
|
Self.observe('after save', async ctx => {
|
||||||
|
@ -332,43 +322,55 @@ module.exports = Self => {
|
||||||
await models.Chat.send(httpCtx, `@${currentWorker.user}`, message);
|
await models.Chat.send(httpCtx, `@${currentWorker.user}`, message);
|
||||||
};
|
};
|
||||||
|
|
||||||
async function validateCreditChange(ctx, finalState) {
|
// Credit change validations
|
||||||
let models = Self.app.models;
|
Self.changeCredit = async function changeCredit(ctx, finalState, changes) {
|
||||||
let userId = ctx.options.accessToken.userId;
|
const models = Self.app.models;
|
||||||
|
const userId = ctx.options.accessToken.userId;
|
||||||
|
|
||||||
let currentUserIsManager = await models.Account.hasRole(userId, 'manager');
|
const isManager = await models.Account.hasRole(userId, 'manager', ctx.options);
|
||||||
if (currentUserIsManager)
|
if (!isManager) {
|
||||||
return;
|
const lastCredit = await models.ClientCredit.findOne({
|
||||||
|
where: {
|
||||||
|
clientFk: finalState.id
|
||||||
|
},
|
||||||
|
order: 'id DESC'
|
||||||
|
}, ctx.options);
|
||||||
|
|
||||||
let filter = {
|
const lastAmount = lastCredit && lastCredit.amount;
|
||||||
|
const lastWorkerId = lastCredit && lastCredit.workerFk;
|
||||||
|
const lastWorkerIsManager = await models.Account.hasRole(lastWorkerId, 'manager', ctx.options);
|
||||||
|
|
||||||
|
if (lastAmount == 0 && lastWorkerIsManager)
|
||||||
|
throw new UserError(`You can't change the credit set to zero from a manager`);
|
||||||
|
|
||||||
|
const creditLimits = await models.ClientCreditLimit.find({
|
||||||
fields: ['roleFk'],
|
fields: ['roleFk'],
|
||||||
where: {
|
where: {
|
||||||
maxAmount: {gt: ctx.data.credit}
|
maxAmount: {gte: changes.credit}
|
||||||
}
|
}
|
||||||
};
|
}, ctx.options);
|
||||||
|
|
||||||
let limits = await models.ClientCreditLimit.find(filter);
|
const requiredRoles = [];
|
||||||
|
for (limit of creditLimits)
|
||||||
if (limits.length == 0)
|
|
||||||
throw new UserError('Credit limits not found');
|
|
||||||
|
|
||||||
// Si el usuario no tiene alguno de los roles no continua
|
|
||||||
|
|
||||||
let requiredRoles = [];
|
|
||||||
for (limit of limits)
|
|
||||||
requiredRoles.push(limit.roleFk);
|
requiredRoles.push(limit.roleFk);
|
||||||
|
|
||||||
let where = {
|
const userRequiredRoles = await models.RoleMapping.count({
|
||||||
roleId: {inq: requiredRoles},
|
roleId: {inq: requiredRoles},
|
||||||
principalType: 'USER',
|
principalType: 'USER',
|
||||||
principalId: userId
|
principalId: userId
|
||||||
};
|
}, ctx.options);
|
||||||
let count = await models.RoleMapping.count(where);
|
|
||||||
|
|
||||||
if (count <= 0)
|
if (userRequiredRoles <= 0)
|
||||||
throw new UserError('The role cannot set this credit amount');
|
throw new UserError(`You don't have enough privileges to set this credit amount`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await models.ClientCredit.create({
|
||||||
|
amount: changes.credit,
|
||||||
|
clientFk: finalState.id,
|
||||||
|
workerFk: userId
|
||||||
|
}, ctx.options);
|
||||||
|
};
|
||||||
|
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
app.on('started', function() {
|
app.on('started', function() {
|
||||||
let account = app.models.Account;
|
let account = app.models.Account;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const models = require('vn-loopback/server/server').models;
|
||||||
const LoopBackContext = require('loopback-context');
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('Client Model', () => {
|
describe('Client Model', () => {
|
||||||
|
@ -14,8 +14,8 @@ describe('Client Model', () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const ctx = {req: activeCtx};
|
const ctx = {req: activeCtx};
|
||||||
const chatModel = app.models.Chat;
|
const chatModel = models.Chat;
|
||||||
const client = {id: 1101, name: 'Bruce Banner'};
|
const instance = {id: 1101, name: 'Bruce Banner'};
|
||||||
const previousWorkerId = 1106; // DavidCharlesHaller
|
const previousWorkerId = 1106; // DavidCharlesHaller
|
||||||
const currentWorkerId = 1107; // HankPym
|
const currentWorkerId = 1107; // HankPym
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ describe('Client Model', () => {
|
||||||
it('should call to the Chat send() method for both workers', async() => {
|
it('should call to the Chat send() method for both workers', async() => {
|
||||||
spyOn(chatModel, 'send').and.callThrough();
|
spyOn(chatModel, 'send').and.callThrough();
|
||||||
|
|
||||||
await app.models.Client.notifyAssignment(client, previousWorkerId, currentWorkerId);
|
await models.Client.notifyAssignment(instance, previousWorkerId, currentWorkerId);
|
||||||
|
|
||||||
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@DavidCharlesHaller', `Client assignment has changed`);
|
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@DavidCharlesHaller', `Client assignment has changed`);
|
||||||
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', `Client assignment has changed`);
|
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', `Client assignment has changed`);
|
||||||
|
@ -38,7 +38,7 @@ describe('Client Model', () => {
|
||||||
it('should call to the Chat send() method for the previous worker', async() => {
|
it('should call to the Chat send() method for the previous worker', async() => {
|
||||||
spyOn(chatModel, 'send').and.callThrough();
|
spyOn(chatModel, 'send').and.callThrough();
|
||||||
|
|
||||||
await app.models.Client.notifyAssignment(client, null, currentWorkerId);
|
await models.Client.notifyAssignment(instance, null, currentWorkerId);
|
||||||
|
|
||||||
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', `Client assignment has changed`);
|
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', `Client assignment has changed`);
|
||||||
});
|
});
|
||||||
|
@ -46,9 +46,69 @@ describe('Client Model', () => {
|
||||||
it('should call to the Chat send() method for the current worker', async() => {
|
it('should call to the Chat send() method for the current worker', async() => {
|
||||||
spyOn(chatModel, 'send').and.callThrough();
|
spyOn(chatModel, 'send').and.callThrough();
|
||||||
|
|
||||||
await app.models.Client.notifyAssignment(client, previousWorkerId, null);
|
await models.Client.notifyAssignment(instance, previousWorkerId, null);
|
||||||
|
|
||||||
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@DavidCharlesHaller', `Client assignment has changed`);
|
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@DavidCharlesHaller', `Client assignment has changed`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('changeCredit()', () => {
|
||||||
|
it('should fail to change the credit as a salesAssistant set to zero by a manager', async() => {
|
||||||
|
const tx = await models.Client.beginTransaction({});
|
||||||
|
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const context = {options};
|
||||||
|
|
||||||
|
// Set credit to zero by a manager
|
||||||
|
const financialBoss = await models.Account.findOne({
|
||||||
|
where: {name: 'financialBoss'}
|
||||||
|
}, options);
|
||||||
|
context.options.accessToken = {userId: financialBoss.id};
|
||||||
|
|
||||||
|
await models.Client.changeCredit(context, instance, {credit: 0});
|
||||||
|
|
||||||
|
const salesAssistant = await models.Account.findOne({
|
||||||
|
where: {name: 'salesAssistant'}
|
||||||
|
}, options);
|
||||||
|
context.options.accessToken = {userId: salesAssistant.id};
|
||||||
|
|
||||||
|
await models.Client.changeCredit(context, instance, {credit: 300});
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(`You can't change the credit set to zero from a manager`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to change to a high credit amount as a salesAssistant', async() => {
|
||||||
|
const tx = await models.Client.beginTransaction({});
|
||||||
|
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const context = {options};
|
||||||
|
|
||||||
|
const salesAssistant = await models.Account.findOne({
|
||||||
|
where: {name: 'salesAssistant'}
|
||||||
|
}, options);
|
||||||
|
context.options.accessToken = {userId: salesAssistant.id};
|
||||||
|
|
||||||
|
await models.Client.changeCredit(context, instance, {credit: 99999});
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(`You don't have enough privileges to set this credit amount`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
vn-auto
|
vn-auto
|
||||||
icon="add_circle"
|
icon="add_circle"
|
||||||
ng-click="$ctrl.onAddEntityClick($event)"
|
vn-click-stop="bankEntity.show({countryFk: $ctrl.client.countryFk})"
|
||||||
vn-tooltip="New bank entity"
|
vn-tooltip="New bank entity"
|
||||||
vn-acl="salesAssistant">
|
vn-acl="salesAssistant">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
|
@ -108,53 +108,8 @@
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- Create bank entity dialog -->
|
<!-- New bankentity dialog -->
|
||||||
<vn-dialog class="edit"
|
<vn-new-bank-entity
|
||||||
vn-id="bankEntityDialog"
|
vn-id="bankEntity"
|
||||||
on-accept="$ctrl.onBankEntityAccept()"
|
on-accept="$ctrl.onAccept($data)">
|
||||||
message="New bank entity">
|
</vn-new-bank-entity>
|
||||||
<tpl-body>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="Name"
|
|
||||||
ng-model="$ctrl.newBankEntity.name"
|
|
||||||
required="true"
|
|
||||||
vn-focus>
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-autocomplete
|
|
||||||
vn-one
|
|
||||||
vn-id="country"
|
|
||||||
label="Country"
|
|
||||||
ng-model="$ctrl.newBankEntity.countryFk"
|
|
||||||
fields="['id', 'country', 'code']"
|
|
||||||
url="Countries"
|
|
||||||
value-field="id"
|
|
||||||
show-field="country"
|
|
||||||
required="true">
|
|
||||||
</vn-autocomplete>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="Entity Code"
|
|
||||||
ng-model="$ctrl.newBankEntity.id"
|
|
||||||
ng-show="country.selection.code === 'ES'">
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-horizontal>
|
|
||||||
<vn-horizontal>
|
|
||||||
<vn-textfield
|
|
||||||
vn-one
|
|
||||||
label="Swift / BIC"
|
|
||||||
ng-model="$ctrl.newBankEntity.bic"
|
|
||||||
required="true">
|
|
||||||
</vn-textfield>
|
|
||||||
</vn-horizontal>
|
|
||||||
</tpl-body>
|
|
||||||
<tpl-buttons>
|
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
|
||||||
<button response="accept" translate>Create</button>
|
|
||||||
</tpl-buttons>
|
|
||||||
</vn-dialog>
|
|
|
@ -37,17 +37,8 @@ export default class Controller extends Section {
|
||||||
return payMethod || iban || dueDay;
|
return payMethod || iban || dueDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddEntityClick(event) {
|
onAccept(data) {
|
||||||
event.preventDefault();
|
this.client.bankEntityFk = data.id;
|
||||||
this.newBankEntity = {
|
|
||||||
countryFk: Number.parseInt(this.client.countryFk)
|
|
||||||
};
|
|
||||||
this.$.bankEntityDialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
onBankEntityAccept() {
|
|
||||||
return this.$http.post(`BankEntities`, this.newBankEntity)
|
|
||||||
.then(res => this.client.bankEntityFk = res.data.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get ibanCountry() {
|
get ibanCountry() {
|
||||||
|
|
|
@ -35,20 +35,12 @@ describe('Client', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('onBankEntityAccept()', () => {
|
describe('onAccept()', () => {
|
||||||
it('should request to create a new bank entity', () => {
|
it('should assign the response id to the client bankEntityFk', () => {
|
||||||
let newBankEntity = {
|
const expectedResponse = {id: 999};
|
||||||
name: 'My new bank entity',
|
controller.onAccept(expectedResponse);
|
||||||
bic: 'ES123',
|
|
||||||
countryFk: 1,
|
|
||||||
id: 999
|
|
||||||
};
|
|
||||||
controller.newBankEntity = newBankEntity;
|
|
||||||
$httpBackend.expectPOST('BankEntities', newBankEntity).respond({id: 999});
|
|
||||||
controller.onBankEntityAccept();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.client.bankEntityFk).toEqual(newBankEntity.id);
|
expect(controller.client.bankEntityFk).toEqual(expectedResponse.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -15,4 +15,3 @@ Received B2B VNL: Recibido B2B VNL
|
||||||
Save: Guardar
|
Save: Guardar
|
||||||
New bank entity: Nueva entidad bancaria
|
New bank entity: Nueva entidad bancaria
|
||||||
Name can't be empty: El nombre no puede quedar vacío
|
Name can't be empty: El nombre no puede quedar vacío
|
||||||
Entity Code: Código
|
|
|
@ -182,8 +182,7 @@
|
||||||
vn-one
|
vn-one
|
||||||
label="Verified data"
|
label="Verified data"
|
||||||
ng-model="$ctrl.client.isTaxDataChecked"
|
ng-model="$ctrl.client.isTaxDataChecked"
|
||||||
vn-acl="salesAssistant"
|
vn-acl="salesAssistant">
|
||||||
disabled="!$ctrl.client.businessTypeFk">
|
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
filter="::$ctrl.ticketFilter"
|
filter="::$ctrl.ticketFilter"
|
||||||
limit="5"
|
limit="5"
|
||||||
data="tickets"
|
data="tickets"
|
||||||
order="shipped DESC">
|
order="shipped DESC, id">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-card class="summary">
|
<vn-card class="summary">
|
||||||
<h5>
|
<h5>
|
||||||
|
@ -314,7 +314,7 @@
|
||||||
{{::ticket.nickname}}
|
{{::ticket.nickname}}
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>
|
<vn-td expand>
|
||||||
{{::ticket.agencyMode.name}}
|
{{::ticket.agencyMode.name}}
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
|
|
|
@ -115,7 +115,6 @@ module.exports = Self => {
|
||||||
const stmts = [];
|
const stmts = [];
|
||||||
let stmt;
|
let stmt;
|
||||||
|
|
||||||
stmts.push('CALL cache.last_buy_refresh(FALSE)');
|
|
||||||
stmts.push('CALL cache.visible_refresh(@calc_id, FALSE, 1)');
|
stmts.push('CALL cache.visible_refresh(@calc_id, FALSE, 1)');
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`
|
stmt = new ParameterizedSQL(`
|
||||||
|
|
|
@ -57,6 +57,11 @@ module.exports = Self => {
|
||||||
arg: 'stemMultiplier',
|
arg: 'stemMultiplier',
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
description: 'The item multiplier',
|
description: 'The item multiplier',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'landed',
|
||||||
|
type: 'date',
|
||||||
|
description: 'The item last buy landed date',
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
|
@ -114,6 +119,8 @@ module.exports = Self => {
|
||||||
return {'ori.code': value};
|
return {'ori.code': value};
|
||||||
case 'intrastat':
|
case 'intrastat':
|
||||||
return {'intr.description': value};
|
return {'intr.description': value};
|
||||||
|
case 'landed':
|
||||||
|
return {'lb.landed': value};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -146,7 +153,8 @@ module.exports = Self => {
|
||||||
ic.name AS category,
|
ic.name AS category,
|
||||||
intr.description AS intrastat,
|
intr.description AS intrastat,
|
||||||
b.grouping,
|
b.grouping,
|
||||||
b.packing
|
b.packing,
|
||||||
|
lb.landing AS landed
|
||||||
FROM item i
|
FROM item i
|
||||||
LEFT JOIN itemType it ON it.id = i.typeFk
|
LEFT JOIN itemType it ON it.id = i.typeFk
|
||||||
LEFT JOIN itemCategory ic ON ic.id = it.categoryFk
|
LEFT JOIN itemCategory ic ON ic.id = it.categoryFk
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
<vn-th field="density" shrink>Density</vn-th>
|
<vn-th field="density" shrink>Density</vn-th>
|
||||||
<vn-th field="stemMultiplier" shrink>Multiplier</vn-th>
|
<vn-th field="stemMultiplier" shrink>Multiplier</vn-th>
|
||||||
<vn-th field="active" shrink>Active</vn-th>
|
<vn-th field="active" shrink>Active</vn-th>
|
||||||
|
<vn-th field="landed" shrink-date>Landed</vn-th>
|
||||||
<vn-th></vn-th>
|
<vn-th></vn-th>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
</vn-thead>
|
</vn-thead>
|
||||||
|
@ -87,6 +88,7 @@
|
||||||
ng-model="::item.isActive">
|
ng-model="::item.isActive">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
|
<vn-td shrink-date>{{::item.landed | date:'dd/MM/yyyy'}}</vn-td>
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
<vn-horizontal class="buttons">
|
<vn-horizontal class="buttons">
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
|
|
|
@ -223,7 +223,8 @@ module.exports = Self => {
|
||||||
MINUTE(z.hour) AS zoneMinute,
|
MINUTE(z.hour) AS zoneMinute,
|
||||||
z.name AS zoneName,
|
z.name AS zoneName,
|
||||||
z.id AS zoneFk,
|
z.id AS zoneFk,
|
||||||
CAST(z.hour AS CHAR) AS hour
|
CAST(z.hour AS CHAR) AS hour,
|
||||||
|
TIME_FORMAT(zed.etc, '%H:%i') AS practicalHour
|
||||||
FROM ticket t
|
FROM ticket t
|
||||||
LEFT JOIN invoiceOut io ON t.refFk = io.ref
|
LEFT JOIN invoiceOut io ON t.refFk = io.ref
|
||||||
LEFT JOIN zone z ON z.id = t.zoneFk
|
LEFT JOIN zone z ON z.id = t.zoneFk
|
||||||
|
@ -235,7 +236,8 @@ module.exports = Self => {
|
||||||
LEFT JOIN state st ON st.id = ts.stateFk
|
LEFT JOIN state st ON st.id = ts.stateFk
|
||||||
LEFT JOIN client c ON c.id = t.clientFk
|
LEFT JOIN client c ON c.id = t.clientFk
|
||||||
LEFT JOIN worker wk ON wk.id = c.salesPersonFk
|
LEFT JOIN worker wk ON wk.id = c.salesPersonFk
|
||||||
LEFT JOIN account.user u ON u.id = wk.userFk`);
|
LEFT JOIN account.user u ON u.id = wk.userFk
|
||||||
|
LEFT JOIN zoneEstimatedDelivery zed ON zed.zoneFk = t.zoneFk`);
|
||||||
|
|
||||||
if (args.orderFk) {
|
if (args.orderFk) {
|
||||||
stmt.merge({
|
stmt.merge({
|
||||||
|
|
|
@ -7,3 +7,5 @@ All the selected elements will be deleted. Are you sure you want to continue?: T
|
||||||
Component lack: Faltan componentes
|
Component lack: Faltan componentes
|
||||||
Minimize/Maximize: Minimizar/Maximizar
|
Minimize/Maximize: Minimizar/Maximizar
|
||||||
Problems: Problemas
|
Problems: Problemas
|
||||||
|
Theoretical: Teórica
|
||||||
|
Practical: Práctica
|
|
@ -37,8 +37,9 @@
|
||||||
<vn-th field="nickname">Client</vn-th>
|
<vn-th field="nickname">Client</vn-th>
|
||||||
<vn-th field="salesPersonFk" class="expendable" shrink>Salesperson</vn-th>
|
<vn-th field="salesPersonFk" class="expendable" shrink>Salesperson</vn-th>
|
||||||
<vn-th field="shipped" shrink-date>Date</vn-th>
|
<vn-th field="shipped" shrink-date>Date</vn-th>
|
||||||
<vn-th>Hour</vn-th>
|
<vn-th>Prep.</vn-th>
|
||||||
<vn-th field="zoneHour" shrink>Closure</vn-th>
|
<vn-th field="hour" shrink>Theoretical</vn-th>
|
||||||
|
<vn-th field="practicalHour">Practical</vn-th>
|
||||||
<vn-th field="provinceFk" class="expendable">Province</vn-th>
|
<vn-th field="provinceFk" class="expendable">Province</vn-th>
|
||||||
<vn-th field="stateFk">State</vn-th>
|
<vn-th field="stateFk">State</vn-th>
|
||||||
<vn-th field="zoneFk">Zone</vn-th>
|
<vn-th field="zoneFk">Zone</vn-th>
|
||||||
|
@ -112,6 +113,7 @@
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>{{::ticket.shipped | date: 'HH:mm'}}</vn-td>
|
<vn-td shrink>{{::ticket.shipped | date: 'HH:mm'}}</vn-td>
|
||||||
<vn-td shrink>{{::ticket.zoneLanding | date: 'HH:mm'}}</vn-td>
|
<vn-td shrink>{{::ticket.zoneLanding | date: 'HH:mm'}}</vn-td>
|
||||||
|
<vn-td shrink>{{::ticket.practicalHour | date: 'HH:mm'}}</vn-td>
|
||||||
<vn-td class="expendable">{{::ticket.province}}</vn-td>
|
<vn-td class="expendable">{{::ticket.province}}</vn-td>
|
||||||
<vn-td class="expendable">
|
<vn-td class="expendable">
|
||||||
<span
|
<span
|
||||||
|
|
|
@ -74,12 +74,8 @@ export default class Controller extends Section {
|
||||||
return {'t.shipped': {
|
return {'t.shipped': {
|
||||||
between: this.dateRange(value)}
|
between: this.dateRange(value)}
|
||||||
};
|
};
|
||||||
case 'id':
|
|
||||||
case 'refFk':
|
|
||||||
case 'zoneFk':
|
case 'zoneFk':
|
||||||
case 'nickname':
|
case 'nickname':
|
||||||
case 'agencyModeFk':
|
|
||||||
case 'warehouseFk':
|
|
||||||
return {[`t.${param}`]: value};
|
return {[`t.${param}`]: value};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,14 @@
|
||||||
label="Beneficiary"
|
label="Beneficiary"
|
||||||
ng-model="supplierAccount.beneficiary"
|
ng-model="supplierAccount.beneficiary"
|
||||||
info="Beneficiary information">
|
info="Beneficiary information">
|
||||||
|
<append>
|
||||||
|
<vn-icon-button
|
||||||
|
vn-auto
|
||||||
|
icon="add_circle"
|
||||||
|
vn-click-stop="bankEntity.show({index: $index})"
|
||||||
|
vn-tooltip="New bank entity">
|
||||||
|
</vn-icon-button>
|
||||||
|
</append>
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-none>
|
<vn-none>
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
|
@ -67,10 +75,11 @@
|
||||||
</vn-submit>
|
</vn-submit>
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- New bankentity dialog -->
|
<!-- New bankentity dialog -->
|
||||||
<vn-new-bank-entity
|
<vn-new-bank-entity
|
||||||
vn-id="bankEntity"
|
vn-id="bankEntity"
|
||||||
on-response="$ctrl.onResponse($response)">
|
on-accept="$ctrl.onAccept($data)">
|
||||||
</vn-new-bank-entity>
|
</vn-new-bank-entity>
|
||||||
|
|
||||||
<vn-confirm
|
<vn-confirm
|
||||||
|
|
|
@ -26,27 +26,10 @@ class Controller extends Section {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onResponse(response) {
|
onAccept(data) {
|
||||||
const data = this.$.model.data;
|
const accounts = this.supplierAccounts;
|
||||||
const supplierAccount = data[this.currentRowIndex];
|
const targetAccount = accounts[data.index];
|
||||||
supplierAccount.bankEntityFk = response.id;
|
targetAccount.bankEntityFk = data.id;
|
||||||
}
|
|
||||||
|
|
||||||
showBankEntity(event, $index) {
|
|
||||||
if (event.defaultPrevented) return;
|
|
||||||
event.preventDefault();
|
|
||||||
this.currentRowIndex = $index;
|
|
||||||
this.$.bankEntity.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
setWireTransfer() {
|
|
||||||
const values = {
|
|
||||||
id: this.$params.id,
|
|
||||||
payMethodFk: this.wireTransferFk
|
|
||||||
};
|
|
||||||
const query = `Suppliers/${this.$params.id}`;
|
|
||||||
return this.$http.patch(query, values)
|
|
||||||
.then(() => this.$.watcher.notifySaved());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
|
|
|
@ -5,17 +5,9 @@ import crudModel from 'core/mocks/crud-model';
|
||||||
describe('Supplier Component vnSupplierAccount', () => {
|
describe('Supplier Component vnSupplierAccount', () => {
|
||||||
let $scope;
|
let $scope;
|
||||||
let controller;
|
let controller;
|
||||||
let $httpBackend;
|
|
||||||
let $httpParamSerializer;
|
|
||||||
let vnApp;
|
|
||||||
|
|
||||||
beforeEach(ngModule('supplier'));
|
beforeEach(ngModule('supplier'));
|
||||||
|
|
||||||
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_, _vnApp_) => {
|
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
|
||||||
vnApp = _vnApp_;
|
|
||||||
$httpBackend = _$httpBackend_;
|
|
||||||
$httpParamSerializer = _$httpParamSerializer_;
|
|
||||||
jest.spyOn(vnApp, 'showError');
|
|
||||||
$scope = $rootScope.$new();
|
$scope = $rootScope.$new();
|
||||||
$scope.model = crudModel;
|
$scope.model = crudModel;
|
||||||
$scope.watcher = watcher;
|
$scope.watcher = watcher;
|
||||||
|
@ -34,58 +26,20 @@ describe('Supplier Component vnSupplierAccount', () => {
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('showBankEntity()', () => {
|
describe('onAccept()', () => {
|
||||||
it('should do nothing if it default is prevented', () => {
|
it('should set the created bank entity id into the target account', () => {
|
||||||
const event = {
|
controller.supplierAccounts = [{}, {}, {}];
|
||||||
defaultPrevented: true,
|
|
||||||
preventDefault: () => {}
|
|
||||||
};
|
|
||||||
jest.spyOn(event, 'preventDefault');
|
|
||||||
jest.spyOn(controller.$.bankEntity, 'open');
|
|
||||||
|
|
||||||
controller.showBankEntity(event);
|
const data = {
|
||||||
|
id: 999,
|
||||||
expect(event.preventDefault).not.toHaveBeenCalledWith();
|
index: 1
|
||||||
expect(controller.$.bankEntity.open).not.toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call preventDefault() and open() when the default is not prevented', () => {
|
|
||||||
const event = {
|
|
||||||
defaultPrevented: false,
|
|
||||||
preventDefault: () => {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.spyOn(event, 'preventDefault');
|
controller.onAccept(data);
|
||||||
jest.spyOn(controller.$.bankEntity, 'open');
|
|
||||||
|
|
||||||
controller.showBankEntity(event);
|
const targetAccount = controller.supplierAccounts[data.index];
|
||||||
|
|
||||||
expect(event.preventDefault).toHaveBeenCalledWith();
|
expect(targetAccount.bankEntityFk).toEqual(data.id);
|
||||||
expect(controller.$.bankEntity.open).toHaveBeenCalledWith();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set pay method to wireTransfer', () => {
|
|
||||||
controller.bankEntity = {
|
|
||||||
name: 'My new bank entity',
|
|
||||||
bic: 'ES1234',
|
|
||||||
countryFk: 1,
|
|
||||||
id: 2200
|
|
||||||
};
|
|
||||||
const expectedParams = {
|
|
||||||
filter: {
|
|
||||||
where: {code: 'wireTransfer'}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const serializedParams = $httpParamSerializer(expectedParams);
|
|
||||||
|
|
||||||
$httpBackend.when('GET', `payMethods/findOne?${serializedParams}`).respond({id: 1});
|
|
||||||
|
|
||||||
const query = `SupplierAccounts/${controller.$.bankEntity.id}/createBankEntity`;
|
|
||||||
$httpBackend.expectPATCH(query).respond({id: 2200});
|
|
||||||
controller.onBankEntityAccept();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.supplierAccount.bankEntityFk).toEqual(controller.bankEntity.id);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
vn-id="model"
|
vn-id="model"
|
||||||
url="Tickets/filter"
|
url="Tickets/filter"
|
||||||
limit="20"
|
limit="20"
|
||||||
order="shippedDate DESC, shippedHour ASC, zoneLanding ASC">
|
order="shippedDate DESC, shippedHour ASC, zoneLanding ASC, id">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
<vn-portal slot="topbar">
|
<vn-portal slot="topbar">
|
||||||
<vn-searchbar
|
<vn-searchbar
|
||||||
|
|
|
@ -78,6 +78,11 @@
|
||||||
</vn-avatar>
|
</vn-avatar>
|
||||||
<span translate>Festive</span>
|
<span translate>Festive</span>
|
||||||
</vn-chip>
|
</vn-chip>
|
||||||
|
<vn-chip>
|
||||||
|
<vn-avatar class="today">
|
||||||
|
</vn-avatar>
|
||||||
|
<span translate>Current day</span>
|
||||||
|
</vn-chip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</vn-side-menu>
|
</vn-side-menu>
|
||||||
|
|
|
@ -8,3 +8,4 @@ days: días
|
||||||
Choose an absence type from the right menu: Elige un tipo de ausencia desde el menú de la derecha
|
Choose an absence type from the right menu: Elige un tipo de ausencia desde el menú de la derecha
|
||||||
To start adding absences, click an absence type from the right menu and then on the day you want to add an absence: Para empezar a añadir ausencias, haz clic en un tipo de ausencia desde el menu de la derecha y después en el día que quieres añadir la ausencia
|
To start adding absences, click an absence type from the right menu and then on the day you want to add an absence: Para empezar a añadir ausencias, haz clic en un tipo de ausencia desde el menu de la derecha y después en el día que quieres añadir la ausencia
|
||||||
You can just add absences within the current year: Solo puedes añadir ausencias dentro del año actual
|
You can just add absences within the current year: Solo puedes añadir ausencias dentro del año actual
|
||||||
|
Current day: Día actual
|
|
@ -42,11 +42,19 @@ vn-worker-calendar {
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.3);
|
border-bottom: 1px solid rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.festive {
|
vn-avatar.festive,
|
||||||
background-color:white;
|
vn-avatar.today {
|
||||||
border: 2px solid $color-alert;
|
background-color: $color-font-dark;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
min-width: 24px;
|
min-width: 24px;
|
||||||
height: 24px
|
height: 24px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vn-avatar.festive {
|
||||||
|
border: 2px solid $color-alert
|
||||||
|
}
|
||||||
|
|
||||||
|
vn-avatar.today {
|
||||||
|
border: 2px solid $color-font-link
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ module.exports = {
|
||||||
throw err;
|
throw err;
|
||||||
}).finally(async() => {
|
}).finally(async() => {
|
||||||
await db.rawSql(`
|
await db.rawSql(`
|
||||||
INSERT INTO vn.mail (sender, replyTo, sent, subject, body, status)
|
INSERT INTO vn.mail (receiver, replyTo, sent, subject, body, status)
|
||||||
VALUES (?, ?, 1, ?, ?, ?)`, [
|
VALUES (?, ?, 1, ?, ?, ?)`, [
|
||||||
options.to,
|
options.to,
|
||||||
options.replyTo,
|
options.replyTo,
|
||||||
|
|
|
@ -143,8 +143,24 @@ module.exports = app => {
|
||||||
AND t.refFk IS NULL
|
AND t.refFk IS NULL
|
||||||
GROUP BY e.ticketFk`, [reqArgs.routeId]);
|
GROUP BY e.ticketFk`, [reqArgs.routeId]);
|
||||||
const ticketIds = tickets.map(ticket => ticket.id);
|
const ticketIds = tickets.map(ticket => ticket.id);
|
||||||
|
|
||||||
await closeAll(ticketIds, reqArgs);
|
await closeAll(ticketIds, reqArgs);
|
||||||
|
|
||||||
|
// Send route report to the agency
|
||||||
|
const agencyMail = await db.findValue(`
|
||||||
|
SELECT am.reportMail
|
||||||
|
FROM route r
|
||||||
|
JOIN agencyMode am ON am.id = r.agencyModeFk
|
||||||
|
WHERE r.id = ?`, [reqArgs.routeId]);
|
||||||
|
|
||||||
|
if (agencyMail) {
|
||||||
|
const args = Object.assign({
|
||||||
|
routeId: reqArgs.routeId,
|
||||||
|
recipient: agencyMail
|
||||||
|
}, reqArgs);
|
||||||
|
|
||||||
|
const email = new Email('driver-route', args);
|
||||||
|
await email.send();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,21 +3,3 @@ title: Orden de recogida
|
||||||
description:
|
description:
|
||||||
dear: Estimado cliente
|
dear: Estimado cliente
|
||||||
instructions: Aqui tienes tu orden de recogida.
|
instructions: Aqui tienes tu orden de recogida.
|
||||||
sections:
|
|
||||||
howToBuy:
|
|
||||||
title: Cómo hacer un pedido
|
|
||||||
description: 'Para realizar un pedido en nuestra web, debes configurarlo indicando:'
|
|
||||||
requeriments:
|
|
||||||
- Si quieres recibir el pedido (por agencia o por nuestro propio reparto) o si
|
|
||||||
lo prefieres recoger en alguno de nuestros almacenes.
|
|
||||||
- La fecha en la que quieres recibir el pedido (se preparará el día anterior).
|
|
||||||
- La dirección de entrega o el almacén donde quieres recoger el pedido.
|
|
||||||
stock: En nuestra web y aplicaciones puedes visualizar el stock disponible de
|
|
||||||
flor cortada, verdes, plantas, complementos y artificial. Ten en cuenta que
|
|
||||||
dicho stock puede variar en función de la fecha seleccionada al configurar el
|
|
||||||
pedido. Es importante CONFIRMAR los pedidos para que la mercancía quede reservada.
|
|
||||||
delivery: El reparto se realiza de lunes a sábado según la zona en la que te encuentres.
|
|
||||||
Por regla general, los pedidos que se entregan por agencia, deben estar confirmados
|
|
||||||
y pagados antes de las 17h del día en que se preparan (el día anterior a recibirlos),
|
|
||||||
aunque esto puede variar si el pedido se envía a través de nuestro reparto y
|
|
||||||
según la zona.
|
|
||||||
|
|
|
@ -118,20 +118,20 @@
|
||||||
</table>
|
</table>
|
||||||
<!-- End of sales block -->
|
<!-- End of sales block -->
|
||||||
|
|
||||||
<div class="columns">
|
<div class="columns vn-mb-ml">
|
||||||
<!-- Services block-->
|
<!-- Services block-->
|
||||||
<div class="size100 no-page-break" v-if="services.length > 0">
|
<div class="size100 no-page-break" v-if="services.length > 0">
|
||||||
<h2>{{$t('services')}}</h2>
|
<h2>{{$t('services.title')}}</h2>
|
||||||
<table class="column-oriented">
|
<table class="column-oriented">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="5%"></th>
|
<th width="5%"></th>
|
||||||
<th class="number">{{$t('quantity')}}</th>
|
<th class="number">{{$t('services.theader.quantity')}}</th>
|
||||||
<th width="50%">{{$t('concept')}}</th>
|
<th width="50%">{{$t('services.theader.concept')}}</th>
|
||||||
<th class="number">{{$t('price')}}</th>
|
<th class="number">{{$t('services.theader.price')}}</th>
|
||||||
<th class="centered" width="5%"></th>
|
<th class="centered" width="5%"></th>
|
||||||
<th class="centered">{{$t('vat')}}</th>
|
<th class="centered">{{$t('services.theader.vat')}}</th>
|
||||||
<th class="number">{{$t('amount')}}</th>
|
<th class="number">{{$t('services.theader.amount')}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -148,25 +148,26 @@
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="6" class="font bold">
|
<td colspan="6" class="font bold">
|
||||||
<span class="pull-right">{{$t('subtotal')}}</span>
|
<span class="pull-right">{{$t('services.tfoot.subtotal')}}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="number">{{serviceTotal | currency('EUR', $i18n.locale)}}</td>
|
<td class="number">{{serviceTotal | currency('EUR', $i18n.locale)}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
|
<span class="font gray">* {{ $t('services.warning') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- End of services block -->
|
<!-- End of services block -->
|
||||||
</div>
|
</div>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<!-- Packages block -->
|
<!-- Packages block -->
|
||||||
<div id="packagings" class="size100 no-page-break" v-if="packagings.length > 0">
|
<div id="packagings" class="size100 no-page-break" v-if="packagings.length > 0">
|
||||||
<h2>{{$t('packagings')}}</h2>
|
<h2>{{$t('packagings.title')}}</h2>
|
||||||
<table class="column-oriented">
|
<table class="column-oriented">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{$t('reference')}}</th>
|
<th>{{$t('packagings.theader.reference')}}</th>
|
||||||
<th class="number">{{$t('quantity')}}</th>
|
<th class="number">{{$t('packagings.theader.quantity')}}</th>
|
||||||
<th wihth="75%">{{$t('concept')}}</th>
|
<th wihth="75%">{{$t('packagings.theader.concept')}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -183,21 +184,18 @@
|
||||||
<div class="columns vn-mt-xl">
|
<div class="columns vn-mt-xl">
|
||||||
<!-- Taxes block -->
|
<!-- Taxes block -->
|
||||||
<div id="taxes" class="size50 pull-right no-page-break" v-if="taxes">
|
<div id="taxes" class="size50 pull-right no-page-break" v-if="taxes">
|
||||||
<!-- <h2>{{$t('taxBreakdown')}}</h2> -->
|
|
||||||
<table class="column-oriented">
|
<table class="column-oriented">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="4">{{$t('taxBreakdown')}}</th>
|
<th colspan="4">{{$t('taxes.title')}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<thead class="light">
|
<thead class="light">
|
||||||
<tr>
|
<tr>
|
||||||
<th width="45%">{{$t('type')}}</th>
|
<th width="45%">{{$t('taxes.theader.type')}}</th>
|
||||||
<th width="25%" class="number">
|
<th width="25%" class="number">{{$t('taxes.theader.taxBase')}}</th>
|
||||||
{{$t('taxBase')}}
|
<th>{{$t('taxes.theader.tax')}}</th>
|
||||||
</th>
|
<th class="number">{{$t('taxes.theader.fee')}}</th>
|
||||||
<th>{{$t('tax')}}</th>
|
|
||||||
<th class="number">{{$t('fee')}}</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -12,17 +12,37 @@ price: PSP/u
|
||||||
discount: Disc.
|
discount: Disc.
|
||||||
vat: VAT
|
vat: VAT
|
||||||
amount: Amount
|
amount: Amount
|
||||||
type: Type
|
|
||||||
taxBase: Tax base
|
|
||||||
tax: Tax
|
|
||||||
fee: Fee
|
|
||||||
total: Total
|
total: Total
|
||||||
subtotal: Subtotal
|
subtotal: Subtotal
|
||||||
taxBreakdown: Tax breakdown
|
|
||||||
packagings: Buckets and packaging
|
|
||||||
services: Services
|
|
||||||
vatType: VAT Type
|
vatType: VAT Type
|
||||||
digitalSignature: Digital signature
|
digitalSignature: Digital signature
|
||||||
ticket: Delivery note {0}
|
ticket: Delivery note {0}
|
||||||
plantPassport: Plant passport
|
plantPassport: Plant passport
|
||||||
packages: Packages
|
packages: Packages
|
||||||
|
services:
|
||||||
|
title: Services
|
||||||
|
theader:
|
||||||
|
quantity: Qty.
|
||||||
|
concept: Concept
|
||||||
|
price: PSP/u
|
||||||
|
vat: VAT
|
||||||
|
amount: Amount
|
||||||
|
tfoot:
|
||||||
|
subtotal: Subtotal
|
||||||
|
warning: Deposit packaging will be invoiced if they have not been returned after 30 days of their delivery.
|
||||||
|
packagings:
|
||||||
|
title: Buckets and packaging
|
||||||
|
theader:
|
||||||
|
reference: Reference
|
||||||
|
quantity: Quantity
|
||||||
|
concept: Concept
|
||||||
|
taxes:
|
||||||
|
title: Tax breakdown
|
||||||
|
theader:
|
||||||
|
type: Type
|
||||||
|
taxBase: Tax base
|
||||||
|
tax: Tax
|
||||||
|
fee: Fee
|
||||||
|
tfoot:
|
||||||
|
subtotal: Subtotal
|
||||||
|
total: Total
|
|
@ -12,17 +12,37 @@ price: PVP/u
|
||||||
discount: Dto.
|
discount: Dto.
|
||||||
vat: IVA
|
vat: IVA
|
||||||
amount: Importe
|
amount: Importe
|
||||||
type: Tipo
|
|
||||||
taxBase: Base imp.
|
|
||||||
tax: Tasa
|
|
||||||
fee: Cuota
|
|
||||||
total: Total
|
total: Total
|
||||||
subtotal: Subtotal
|
subtotal: Subtotal
|
||||||
taxBreakdown: Desglose impositivo
|
|
||||||
packagings: Cubos y embalajes
|
|
||||||
services: Servicios
|
|
||||||
vatType: Tipo de IVA
|
vatType: Tipo de IVA
|
||||||
digitalSignature: Firma digital
|
digitalSignature: Firma digital
|
||||||
ticket: Albarán {0}
|
ticket: Albarán {0}
|
||||||
plantPassport: Pasaporte fitosanitario
|
plantPassport: Pasaporte fitosanitario
|
||||||
packages: Bultos
|
packages: Bultos
|
||||||
|
services:
|
||||||
|
title: Servicios
|
||||||
|
theader:
|
||||||
|
quantity: Cantidad
|
||||||
|
concept: Concepto
|
||||||
|
price: PVP/u
|
||||||
|
vat: IVA
|
||||||
|
amount: Importe
|
||||||
|
tfoot:
|
||||||
|
subtotal: Subtotal
|
||||||
|
warning: Los embalajes en depósito se facturarán si no han sido devueltos pasados 30 dias de su entrega.
|
||||||
|
packagings:
|
||||||
|
title: Cubos y embalajes
|
||||||
|
theader:
|
||||||
|
reference: Referencia
|
||||||
|
quantity: Cantidad
|
||||||
|
concept: Concepto
|
||||||
|
taxes:
|
||||||
|
title: Desglose impositivo
|
||||||
|
theader:
|
||||||
|
type: Tipo
|
||||||
|
taxBase: Base imp.
|
||||||
|
tax: Tasa
|
||||||
|
fee: Cuota
|
||||||
|
tfoot:
|
||||||
|
subtotal: Subtotal
|
||||||
|
total: Total
|
|
@ -12,17 +12,37 @@ price: PRIX/u
|
||||||
discount: Remise
|
discount: Remise
|
||||||
vat: TVA
|
vat: TVA
|
||||||
amount: Montant
|
amount: Montant
|
||||||
type: Type
|
|
||||||
taxBase: Base imposable
|
|
||||||
tax: Taxe
|
|
||||||
fee: Quote
|
|
||||||
total: Total
|
total: Total
|
||||||
subtotal: Total partiel
|
subtotal: Total partiel
|
||||||
taxBreakdown: Répartition taxes
|
|
||||||
packagings: Bacs et emballages
|
|
||||||
services: Service
|
|
||||||
vatType: Type de TVA
|
vatType: Type de TVA
|
||||||
digitalSignature: Signature numérique
|
digitalSignature: Signature numérique
|
||||||
ticket: BL {0}
|
ticket: BL {0}
|
||||||
plantPassport: Passeport phytosanitaire
|
plantPassport: Passeport phytosanitaire
|
||||||
packages: Paquets
|
packages: Paquets
|
||||||
|
services:
|
||||||
|
title: Service
|
||||||
|
theader:
|
||||||
|
quantity: Quantité
|
||||||
|
concept: Concept
|
||||||
|
price: PRIX/u
|
||||||
|
vat: TVA
|
||||||
|
amount: Montant
|
||||||
|
tfoot:
|
||||||
|
subtotal: Total partiel
|
||||||
|
warning: Les emballages de consigne seront facturés s'ils n'ont pas été retournés après 30 jours de leur livraison.
|
||||||
|
packagings:
|
||||||
|
title: Bacs et emballages
|
||||||
|
theader:
|
||||||
|
reference: Référence
|
||||||
|
quantity: Quantité
|
||||||
|
concept: Concept
|
||||||
|
taxes:
|
||||||
|
title: Répartition taxes
|
||||||
|
theader:
|
||||||
|
type: Type
|
||||||
|
taxBase: Base imposable
|
||||||
|
tax: Taxe
|
||||||
|
fee: Quote
|
||||||
|
tfoot:
|
||||||
|
subtotal: Total partiel
|
||||||
|
total: Total
|
|
@ -12,17 +12,37 @@ price: PVP/u
|
||||||
discount: Dto.
|
discount: Dto.
|
||||||
vat: IVA
|
vat: IVA
|
||||||
amount: Importe
|
amount: Importe
|
||||||
type: Tipo
|
|
||||||
taxBase: Base imp.
|
|
||||||
tax: Taxa
|
|
||||||
fee: Quota
|
|
||||||
total: Total
|
total: Total
|
||||||
subtotal: Sub-total
|
subtotal: Sub-total
|
||||||
taxBreakdown: Desglose impositivo
|
|
||||||
packagings: Baldes e Embalagens
|
|
||||||
services: Serviços
|
|
||||||
vatType: Tipo de IVA
|
vatType: Tipo de IVA
|
||||||
digitalSignature: Assinatura digital
|
digitalSignature: Assinatura digital
|
||||||
ticket: Nota de Entrega {0}
|
ticket: Nota de Entrega {0}
|
||||||
plantPassport: Passaporte vegetal
|
plantPassport: Passaporte vegetal
|
||||||
packages: Pacotes
|
packages: Pacotes
|
||||||
|
services:
|
||||||
|
title: Serviços
|
||||||
|
theader:
|
||||||
|
quantity: Quantidade
|
||||||
|
concept: Conceito
|
||||||
|
price: PVP/u
|
||||||
|
vat: IVA
|
||||||
|
amount: Quantia
|
||||||
|
tfoot:
|
||||||
|
subtotal: Subtotal
|
||||||
|
warning: As embalagens em depósito serão facturadas e cobradas se não são devolvidas 30 dias após a entrega.
|
||||||
|
packagings:
|
||||||
|
title: Baldes e Embalagens
|
||||||
|
theader:
|
||||||
|
reference: Referência
|
||||||
|
quantity: Quantidade
|
||||||
|
concept: Conceito
|
||||||
|
taxes:
|
||||||
|
title: Repartição de impostos
|
||||||
|
theader:
|
||||||
|
type: Cara
|
||||||
|
taxBase: Tributável
|
||||||
|
tax: Taxa
|
||||||
|
fee: Compartilhado
|
||||||
|
tfoot:
|
||||||
|
subtotal: Subtotal
|
||||||
|
total: Total
|
Loading…
Reference in New Issue