diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index a3c9c2da4..2b65efbc1 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -6,6 +6,11 @@ ALTER TABLE `vn`.`address` AUTO_INCREMENT = 1; ALTER TABLE `vn`.`zoneGeo` 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`) VALUES ('1', '6'); @@ -125,14 +130,14 @@ INSERT INTO `vn`.`warehouseAlias`(`id`, `name`) (1, 'Main Warehouse'), (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 - (1, 'Warehouse One', 'ALG', 1, 1, 1, 1, 1, 1, 1, 2, 1), - (2, 'Warehouse Two', NULL, 1, 1, 1, 1, 0, 0, 1, 2, 13), - (3, 'Warehouse Three', NULL, 1, 1, 1, 1, 0, 0, 0, 2, 1), - (4, 'Warehouse Four', NULL, 1, 1, 1, 1, 0, 0, 0, 2, 1), - (5, 'Warehouse Five', NULL, 1, 1, 1, 1, 0, 0, 0, 2, 1), - (13, 'Inventory', NULL, 1, 1, 1, 0, 0, 0, 0, 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, 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, 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, 0); INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `isPreviousPreparedByPacking`, `code`, `pickingPlacement`, `path`) 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` = 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; @@ -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), (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), - (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`) VALUES @@ -2409,4 +2414,6 @@ INSERT INTO `vn`.`expeditionScan` (`id`, `expeditionFk`, `scanned`, `palletFk`) (7, 7, CURDATE(), 1), (8, 8, CURDATE(), 1), (9, 9, CURDATE(), 1), - (10, 10, CURDATE(), 1); \ No newline at end of file + (10, 10, CURDATE(), 1); + +CALL `cache`.`last_buy_refresh`(FALSE); \ No newline at end of file diff --git a/db/tests/cache/last_buy_refresh.spec.js b/db/tests/cache/last_buy_refresh.spec.js index 0036e2762..71a9c3872 100644 --- a/db/tests/cache/last_buy_refresh.spec.js +++ b/db/tests/cache/last_buy_refresh.spec.js @@ -3,22 +3,18 @@ const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; describe('last_buy_refresh()', () => { it(`should store some data on cache.last_buy`, async() => { - let stmts = []; - let stmt; + const stmts = []; stmts.push('START TRANSACTION'); - stmt = new ParameterizedSQL('CALL cache.last_buy_refresh(true)'); - stmts.push(stmt); - - let lastBuyTableIndex = stmts.push(`SELECT * FROM cache.last_buy ORDER BY item_id ASC`) - 1; + const lastBuyTableIndex = stmts.push(`SELECT * FROM cache.last_buy ORDER BY item_id ASC`) - 1; stmts.push('ROLLBACK'); - let sql = ParameterizedSQL.join(stmts, ';'); - let result = await app.models.Ticket.rawStmt(sql); + const sql = ParameterizedSQL.join(stmts, ';'); + const result = await app.models.Ticket.rawStmt(sql); - let lastBuyTable = result[lastBuyTableIndex]; + const lastBuyTable = result[lastBuyTableIndex]; expect(lastBuyTable.length).toEqual(12); diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 6f257774a..f63d67f8b 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -186,9 +186,10 @@ export default { receivedB2BVNLCheckbox: 'vn-client-billing-data vn-check[label="Received B2B VNL"]', 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', - newBankEntityName: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.newBankEntity.name"]', - newBankEntityBIC: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.newBankEntity.bic"]', - newBankEntityCode: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.newBankEntity.id"]', + newBankEntityName: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.data.name"]', + newBankEntityBIC: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.data.bic"]', + 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"]', saveButton: 'vn-client-billing-data button[type=submit]', watcher: 'vn-client-billing-data vn-watcher' diff --git a/e2e/paths/02-client/04_edit_billing_data.spec.js b/e2e/paths/02-client/04_edit_billing_data.spec.js index 208345457..6bc48093e 100644 --- a/e2e/paths/02-client/04_edit_billing_data.spec.js +++ b/e2e/paths/02-client/04_edit_billing_data.spec.js @@ -34,8 +34,9 @@ describe('Client Edit billing data path', () => { it(`should create a new BIC code`, async() => { await page.waitToClick(selectors.clientBillingData.newBankEntityButton); 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.autocompleteSearch(selectors.clientBillingData.newBankEntityCountry, 'España'); + await page.write(selectors.clientBillingData.newBankEntityCode, '9999'); await page.waitToClick(selectors.clientBillingData.acceptBankEntityButton); await page.waitForTextInField(selectors.clientBillingData.swiftBic, 'Gotham City Bank'); const newcode = await page.waitToGetProperty(selectors.clientBillingData.swiftBic, 'value'); diff --git a/front/core/components/calendar/index.html b/front/core/components/calendar/index.html index eb6d196ba..086fe4338 100644 --- a/front/core/components/calendar/index.html +++ b/front/core/components/calendar/index.html @@ -19,28 +19,42 @@ ng-if="$ctrl.displayControls"> -
-
- {{::day.localeChar}} -
+
+
+
+
+ {{::day.localeChar}} +
+
-
-
-
- {{::day | date: 'd'}} -
-
+ +
+
+
+
+ {{::week}} +
+
+
+
+
+
+ {{::day | date: 'd'}} +
+
+
\ No newline at end of file diff --git a/front/core/components/calendar/index.js b/front/core/components/calendar/index.js index 17bf52941..85b51fd04 100644 --- a/front/core/components/calendar/index.js +++ b/front/core/components/calendar/index.js @@ -12,11 +12,12 @@ import './style.scss'; * @event move Emitted when month changes */ export default class Calendar extends FormInput { - constructor($element, $scope, vnWeekDays) { + constructor($element, $scope, vnWeekDays, moment) { super($element, $scope); this.weekDays = vnWeekDays.locales; this.defaultDate = new Date(); 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. */ repaint() { 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()); - dayIndex.setDate(1 - weekdayOffset); + dayIndex.setDate(1 - this.weekdayOffset); this.days = []; @@ -70,27 +79,55 @@ export default class Calendar extends FormInput { this.days.push(new Date(dayIndex.getTime())); 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. * - * @param {Date} day The day + * @param {Date} date The date * @return {Object} The CSS classes to apply */ - getDayClasses(day) { - let wday = day.getDay(); - let month = day.getMonth(); + getDayClasses(date) { + let day = date.getDate(); + let wday = date.getDay(); + let month = date.getMonth(); + + const currentDay = new Date().getDate(); + const currentMonth = new Date().getMonth(); let classes = { + today: day === currentDay && month === currentMonth, weekend: wday === 6 || wday === 0, previous: month < this.month, current: 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; 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', { template: require('./index.html'), @@ -193,6 +230,7 @@ ngModule.vnComponent('vnCalendar', { formatDay: '&?', displayControls: ' .weekdays { + & #days-header { + flex-direction: row; + display: flex + } + & #days-header > .week-numbers { + width: 10% + } + & #days-header > .weekdays { display: flex; color: $color-font-secondary; margin-bottom: 8px; @@ -27,17 +34,49 @@ font-weight: bold; font-size: .8rem; text-align: center; + width: 90%; & > section { width: 14.28%; 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; + justify-content: 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; + width: 90%; & > .day { width: 14.28%; @@ -46,6 +85,17 @@ justify-content: 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 { color: $color-font-secondary; } diff --git a/front/core/vendor.js b/front/core/vendor.js index d7e7f1e63..1c6d3217e 100644 --- a/front/core/vendor.js +++ b/front/core/vendor.js @@ -9,13 +9,15 @@ import 'angular-translate-loader-partial'; import '@uirouter/angularjs'; import 'mg-crud'; import 'oclazyload'; +import 'angular-moment'; export const ngDeps = [ 'ngAnimate', 'pascalprecht.translate', 'ui.router', 'mgCrud', - 'oc.lazyLoad' + 'oc.lazyLoad', + 'angularMoment' ]; import * as validator from 'validator'; diff --git a/front/package-lock.json b/front/package-lock.json index 48585ff69..1eabc2a6e 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -3,6 +3,181 @@ "version": "1.0.0", "lockfileVersion": 1, "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": { "@uirouter/angularjs": { "version": "1.0.29", @@ -27,6 +202,14 @@ "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.2.tgz", "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": { "version": "2.18.4", "resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.18.4.tgz", @@ -78,6 +261,11 @@ "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": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/oclazyload/-/oclazyload-0.6.3.tgz", diff --git a/front/package.json b/front/package.json index 89088cd7a..77af30f6c 100644 --- a/front/package.json +++ b/front/package.json @@ -12,6 +12,7 @@ "@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", diff --git a/front/salix/components/bank-entity/index.html b/front/salix/components/bank-entity/index.html index 3a7786607..211b77317 100644 --- a/front/salix/components/bank-entity/index.html +++ b/front/salix/components/bank-entity/index.html @@ -1,40 +1,46 @@ - - -

Please, ensure you put the correct data!

- - - - - - - - - - -
- - - - -
+ + New bank entity + + +

Please, ensure you put the correct data!

+ + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/front/salix/components/bank-entity/index.js b/front/salix/components/bank-entity/index.js index 192ebe046..261018017 100644 --- a/front/salix/components/bank-entity/index.js +++ b/front/salix/components/bank-entity/index.js @@ -1,35 +1,24 @@ import ngModule from '../../module'; -import Component from 'core/lib/component'; +import Dialog from 'core/components/dialog'; import './style.scss'; -class Controller extends Component { - open() { - this.$.bankEntityDialog.show(); - } +class Controller extends Dialog { + responseHandler(response) { + if (response !== 'accept') + return super.responseHandler(response); - resetData() { - this.data = {}; - } + if (!this.data.countryFk) + throw new Error(`The country can't be empty`); - onAccept() { - try { - if (!this.data.countryFk) - throw new Error(`The country can't be empty`); - - this.$http.post(`bankEntities`, this.data).then(res => { - this.vnApp.showMessage(this.$t('The bank entity has been created. You can save the data now')); - this.emit('response', {$response: res.data}); - }); - } catch (e) { - this.vnApp.showError(this.$t(e.message)); - return false; - } - return true; + return this.$http.post(`bankEntities`, this.data) + .then(res => this.data.id = res.data.id) + .then(() => super.responseHandler(response)) + .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))); } } ngModule.vnComponent('vnNewBankEntity', { - template: require('./index.html'), + slotTemplate: require('./index.html'), controller: Controller, bindings: { data: '<', diff --git a/front/salix/components/bank-entity/index.spec.js b/front/salix/components/bank-entity/index.spec.js index c288c3052..f93325231 100644 --- a/front/salix/components/bank-entity/index.spec.js +++ b/front/salix/components/bank-entity/index.spec.js @@ -5,49 +5,35 @@ describe('Salix Component vnNewBankEntity', () => { let $httpBackend; let $scope; let $element; - let vnApp; beforeEach(ngModule('salix')); - beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _vnApp_) => { + beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => { $httpBackend = _$httpBackend_; - vnApp = _vnApp_; - jest.spyOn(vnApp, 'showError'); $scope = $rootScope.$new(); - $element = angular.element(''); - controller = $componentController('vnNewBankEntity', {$element, $scope}); + $element = angular.element(''); + const $transclude = { + $$boundTransclude: { + $$slots: [] + } + }; + controller = $componentController('vnNewBankEntity', {$element, $scope, $transclude}); + controller.vnApp = {showSuccess: jest.fn()}; })); - describe('resetData()', () => { - it('should reset the location in the controller', () => { - 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', () => { + describe('responseHandler()', () => { + it('should show a success message after the query to bankEntities', () => { controller.data = { 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(); + + expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!'); + expect(controller.data.id).toEqual(1); }); }); }); diff --git a/front/salix/components/bank-entity/locale/es.yml b/front/salix/components/bank-entity/locale/es.yml index fe5160572..874a42393 100644 --- a/front/salix/components/bank-entity/locale/es.yml +++ b/front/salix/components/bank-entity/locale/es.yml @@ -9,4 +9,5 @@ The country can't be empty: El país no puede quedar vacío The postcode has been created. You can save the data now: Se ha creado el código postal. Ahora puedes guardar los datos The city has been created: Se ha creado la ciudad 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 \ No newline at end of file +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 \ No newline at end of file diff --git a/front/salix/components/upload-photo/index.html b/front/salix/components/upload-photo/index.html index 09b36d531..3dd6cdb27 100644 --- a/front/salix/components/upload-photo/index.html +++ b/front/salix/components/upload-photo/index.html @@ -4,7 +4,7 @@ message="Edit photo"> - + - + + + + + + + @@ -37,6 +51,14 @@ + + + + this.editor.bind({url: e.target.result}); - reader.readAsDataURL(value[0]); + if (this.uploadMethod == 'computer') { + const reader = new FileReader(); + reader.onload = e => this.editor.bind({url: e.target.result}); + reader.readAsDataURL(value); + } else if (this.uploadMethod == 'URL') + this.editor.bind({url: value}); } } diff --git a/front/salix/components/upload-photo/index.spec.js b/front/salix/components/upload-photo/index.spec.js index e8ac05fd4..f2aad6a5c 100644 --- a/front/salix/components/upload-photo/index.spec.js +++ b/front/salix/components/upload-photo/index.spec.js @@ -24,17 +24,30 @@ describe('Salix', () => { }); describe('viewportSelection()', () => { - it('should call to displayEditor() and updatePhotoPreview() methods', () => { - controller.displayEditor = jest.fn(); + it('should call to the updatePhotoPreview() method when uploadMethod property is set to "computer"', () => { controller.updatePhotoPreview = jest.fn(); const files = [{name: 'test.jpg'}]; controller.newPhoto.files = files; + controller.uploadMethod = 'computer'; controller.viewportSelection = {code: 'normal'}; - expect(controller.displayEditor).toHaveBeenCalledWith(); - expect(controller.updatePhotoPreview).toHaveBeenCalledWith(files); + const firstFile = files[0]; + + 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); }); }); diff --git a/front/salix/components/upload-photo/locale/es.yml b/front/salix/components/upload-photo/locale/es.yml index bba3a985a..bcc3801d8 100644 --- a/front/salix/components/upload-photo/locale/es.yml +++ b/front/salix/components/upload-photo/locale/es.yml @@ -3,4 +3,6 @@ Select an image: Selecciona una imagen File name: Nombre del fichero Rotate left: Girar a la izquierda Rotate right: Girar a la derecha -Panoramic: Panorámico \ No newline at end of file +Panoramic: Panorámico +Select from computer: Seleccionar desde ordenador +Import from external URL: Importar desde URL externa \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 884db41f0..7a670d2ed 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -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 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", - "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" } \ No newline at end of file diff --git a/modules/client/back/models/client.js b/modules/client/back/models/client.js index a4332290d..6519cb979 100644 --- a/modules/client/back/models/client.js +++ b/modules/client/back/models/client.js @@ -217,19 +217,9 @@ module.exports = Self => { if (isTaxDataCheckedChanged && !orgData.businessTypeFk) throw new UserError(`Can't verify data unless the client has a business type`); } - - if (changes.credit !== undefined) { - await validateCreditChange(ctx, finalState); - 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); - } + // Credit changes + if (changes.credit !== undefined) + await Self.changeCredit(ctx, finalState, changes); }); Self.observe('after save', async ctx => { @@ -332,42 +322,54 @@ module.exports = Self => { await models.Chat.send(httpCtx, `@${currentWorker.user}`, message); }; - async function validateCreditChange(ctx, finalState) { - let models = Self.app.models; - let userId = ctx.options.accessToken.userId; + // Credit change validations + Self.changeCredit = async function changeCredit(ctx, finalState, changes) { + const models = Self.app.models; + const userId = ctx.options.accessToken.userId; - let currentUserIsManager = await models.Account.hasRole(userId, 'manager'); - if (currentUserIsManager) - return; + const isManager = await models.Account.hasRole(userId, 'manager', ctx.options); + if (!isManager) { + const lastCredit = await models.ClientCredit.findOne({ + where: { + clientFk: finalState.id + }, + order: 'id DESC' + }, ctx.options); - let filter = { - fields: ['roleFk'], - where: { - maxAmount: {gt: ctx.data.credit} - } - }; + const lastAmount = lastCredit && lastCredit.amount; + const lastWorkerId = lastCredit && lastCredit.workerFk; + const lastWorkerIsManager = await models.Account.hasRole(lastWorkerId, 'manager', ctx.options); - let limits = await models.ClientCreditLimit.find(filter); + if (lastAmount == 0 && lastWorkerIsManager) + throw new UserError(`You can't change the credit set to zero from a manager`); - if (limits.length == 0) - throw new UserError('Credit limits not found'); + const creditLimits = await models.ClientCreditLimit.find({ + fields: ['roleFk'], + where: { + maxAmount: {gte: changes.credit} + } + }, ctx.options); - // Si el usuario no tiene alguno de los roles no continua + const requiredRoles = []; + for (limit of creditLimits) + requiredRoles.push(limit.roleFk); - let requiredRoles = []; - for (limit of limits) - requiredRoles.push(limit.roleFk); + const userRequiredRoles = await models.RoleMapping.count({ + roleId: {inq: requiredRoles}, + principalType: 'USER', + principalId: userId + }, ctx.options); - let where = { - roleId: {inq: requiredRoles}, - principalType: 'USER', - principalId: userId - }; - let count = await models.RoleMapping.count(where); + if (userRequiredRoles <= 0) + throw new UserError(`You don't have enough privileges to set this credit amount`); + } - if (count <= 0) - throw new UserError('The role cannot 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'); app.on('started', function() { diff --git a/modules/client/back/models/specs/client.spec.js b/modules/client/back/models/specs/client.spec.js index 0406cfc21..d1a6d77cb 100644 --- a/modules/client/back/models/specs/client.spec.js +++ b/modules/client/back/models/specs/client.spec.js @@ -1,4 +1,4 @@ -const app = require('vn-loopback/server/server'); +const models = require('vn-loopback/server/server').models; const LoopBackContext = require('loopback-context'); describe('Client Model', () => { @@ -14,8 +14,8 @@ describe('Client Model', () => { } }; const ctx = {req: activeCtx}; - const chatModel = app.models.Chat; - const client = {id: 1101, name: 'Bruce Banner'}; + const chatModel = models.Chat; + const instance = {id: 1101, name: 'Bruce Banner'}; const previousWorkerId = 1106; // DavidCharlesHaller const currentWorkerId = 1107; // HankPym @@ -29,7 +29,7 @@ describe('Client Model', () => { it('should call to the Chat send() method for both workers', async() => { 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, '@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() => { 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`); }); @@ -46,9 +46,69 @@ describe('Client Model', () => { it('should call to the Chat send() method for the current worker', async() => { 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`); }); }); + + 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`); + }); + }); }); diff --git a/modules/client/front/billing-data/index.html b/modules/client/front/billing-data/index.html index b9c20ec24..ff2e2f157 100644 --- a/modules/client/front/billing-data/index.html +++ b/modules/client/front/billing-data/index.html @@ -66,7 +66,7 @@ @@ -108,53 +108,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + \ No newline at end of file diff --git a/modules/client/front/billing-data/index.js b/modules/client/front/billing-data/index.js index 2dda347b9..7056fa566 100644 --- a/modules/client/front/billing-data/index.js +++ b/modules/client/front/billing-data/index.js @@ -37,17 +37,8 @@ export default class Controller extends Section { return payMethod || iban || dueDay; } - onAddEntityClick(event) { - event.preventDefault(); - 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); + onAccept(data) { + this.client.bankEntityFk = data.id; } get ibanCountry() { diff --git a/modules/client/front/billing-data/index.spec.js b/modules/client/front/billing-data/index.spec.js index 95700cb5a..2e9e8ba97 100644 --- a/modules/client/front/billing-data/index.spec.js +++ b/modules/client/front/billing-data/index.spec.js @@ -35,20 +35,12 @@ describe('Client', () => { }); }); - describe('onBankEntityAccept()', () => { - it('should request to create a new bank entity', () => { - let newBankEntity = { - name: 'My new bank entity', - bic: 'ES123', - countryFk: 1, - id: 999 - }; - controller.newBankEntity = newBankEntity; - $httpBackend.expectPOST('BankEntities', newBankEntity).respond({id: 999}); - controller.onBankEntityAccept(); - $httpBackend.flush(); + describe('onAccept()', () => { + it('should assign the response id to the client bankEntityFk', () => { + const expectedResponse = {id: 999}; + controller.onAccept(expectedResponse); - expect(controller.client.bankEntityFk).toEqual(newBankEntity.id); + expect(controller.client.bankEntityFk).toEqual(expectedResponse.id); }); }); diff --git a/modules/client/front/billing-data/locale/es.yml b/modules/client/front/billing-data/locale/es.yml index a7ecbbdae..0052ee403 100644 --- a/modules/client/front/billing-data/locale/es.yml +++ b/modules/client/front/billing-data/locale/es.yml @@ -14,5 +14,4 @@ Received core VNL: Recibido core VNL Received B2B VNL: Recibido B2B VNL Save: Guardar New bank entity: Nueva entidad bancaria -Name can't be empty: El nombre no puede quedar vacío -Entity Code: Código \ No newline at end of file +Name can't be empty: El nombre no puede quedar vacío \ No newline at end of file diff --git a/modules/client/front/fiscal-data/index.html b/modules/client/front/fiscal-data/index.html index 93a3c9cb6..f3fd42e76 100644 --- a/modules/client/front/fiscal-data/index.html +++ b/modules/client/front/fiscal-data/index.html @@ -182,8 +182,7 @@ vn-one label="Verified data" ng-model="$ctrl.client.isTaxDataChecked" - vn-acl="salesAssistant" - disabled="!$ctrl.client.businessTypeFk"> + vn-acl="salesAssistant"> diff --git a/modules/client/front/summary/index.html b/modules/client/front/summary/index.html index 81f80d370..3c5a8d3e5 100644 --- a/modules/client/front/summary/index.html +++ b/modules/client/front/summary/index.html @@ -5,7 +5,7 @@ filter="::$ctrl.ticketFilter" limit="5" data="tickets" - order="shipped DESC"> + order="shipped DESC, id">
@@ -314,7 +314,7 @@ {{::ticket.nickname}} - + {{::ticket.agencyMode.name}} diff --git a/modules/entry/back/methods/entry/latestBuysFilter.js b/modules/entry/back/methods/entry/latestBuysFilter.js index d59e34e64..cbf9e3b6a 100644 --- a/modules/entry/back/methods/entry/latestBuysFilter.js +++ b/modules/entry/back/methods/entry/latestBuysFilter.js @@ -115,7 +115,6 @@ module.exports = Self => { const stmts = []; let stmt; - stmts.push('CALL cache.last_buy_refresh(FALSE)'); stmts.push('CALL cache.visible_refresh(@calc_id, FALSE, 1)'); stmt = new ParameterizedSQL(` diff --git a/modules/item/back/methods/item/filter.js b/modules/item/back/methods/item/filter.js index f06fd0db5..cff36a223 100644 --- a/modules/item/back/methods/item/filter.js +++ b/modules/item/back/methods/item/filter.js @@ -57,6 +57,11 @@ module.exports = Self => { arg: 'stemMultiplier', type: 'integer', description: 'The item multiplier', + }, + { + arg: 'landed', + type: 'date', + description: 'The item last buy landed date', } ], returns: { @@ -114,6 +119,8 @@ module.exports = Self => { return {'ori.code': value}; case 'intrastat': return {'intr.description': value}; + case 'landed': + return {'lb.landed': value}; } }); @@ -146,7 +153,8 @@ module.exports = Self => { ic.name AS category, intr.description AS intrastat, b.grouping, - b.packing + b.packing, + lb.landing AS landed FROM item i LEFT JOIN itemType it ON it.id = i.typeFk LEFT JOIN itemCategory ic ON ic.id = it.categoryFk diff --git a/modules/item/front/index/index.html b/modules/item/front/index/index.html index 36ef4839a..5b1e7fdc8 100644 --- a/modules/item/front/index/index.html +++ b/modules/item/front/index/index.html @@ -26,6 +26,7 @@ Density Multiplier Active + Landed @@ -87,6 +88,7 @@ ng-model="::item.isActive"> + {{::item.landed | date:'dd/MM/yyyy'}} { MINUTE(z.hour) AS zoneMinute, z.name AS zoneName, 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 LEFT JOIN invoiceOut io ON t.refFk = io.ref 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 client c ON c.id = t.clientFk 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) { stmt.merge({ diff --git a/modules/monitor/front/index/locale/es.yml b/modules/monitor/front/index/locale/es.yml index 160392245..4eef3c93f 100644 --- a/modules/monitor/front/index/locale/es.yml +++ b/modules/monitor/front/index/locale/es.yml @@ -6,4 +6,6 @@ Delete selected elements: Eliminar los elementos seleccionados All the selected elements will be deleted. Are you sure you want to continue?: Todos los elementos seleccionados serán eliminados. ¿Seguro que quieres continuar? Component lack: Faltan componentes Minimize/Maximize: Minimizar/Maximizar -Problems: Problemas \ No newline at end of file +Problems: Problemas +Theoretical: Teórica +Practical: Práctica \ No newline at end of file diff --git a/modules/monitor/front/index/tickets/index.html b/modules/monitor/front/index/tickets/index.html index 2d2c5fa78..82adf2765 100644 --- a/modules/monitor/front/index/tickets/index.html +++ b/modules/monitor/front/index/tickets/index.html @@ -37,8 +37,9 @@ Client Salesperson Date - Hour - Closure + Prep. + Theoretical + Practical Province State Zone @@ -112,6 +113,7 @@ {{::ticket.shipped | date: 'HH:mm'}} {{::ticket.zoneLanding | date: 'HH:mm'}} + {{::ticket.practicalHour | date: 'HH:mm'}} {{::ticket.province}} + + + + + + on-accept="$ctrl.onAccept($data)"> this.$.watcher.notifySaved()); + onAccept(data) { + const accounts = this.supplierAccounts; + const targetAccount = accounts[data.index]; + targetAccount.bankEntityFk = data.id; } onSubmit() { diff --git a/modules/supplier/front/account/index.spec.js b/modules/supplier/front/account/index.spec.js index 6491da7d0..ba21801e1 100644 --- a/modules/supplier/front/account/index.spec.js +++ b/modules/supplier/front/account/index.spec.js @@ -5,17 +5,9 @@ import crudModel from 'core/mocks/crud-model'; describe('Supplier Component vnSupplierAccount', () => { let $scope; let controller; - let $httpBackend; - let $httpParamSerializer; - let vnApp; - beforeEach(ngModule('supplier')); - beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_, _vnApp_) => { - vnApp = _vnApp_; - $httpBackend = _$httpBackend_; - $httpParamSerializer = _$httpParamSerializer_; - jest.spyOn(vnApp, 'showError'); + beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => { $scope = $rootScope.$new(); $scope.model = crudModel; $scope.watcher = watcher; @@ -34,58 +26,20 @@ describe('Supplier Component vnSupplierAccount', () => { }; })); - describe('showBankEntity()', () => { - it('should do nothing if it default is prevented', () => { - const event = { - defaultPrevented: true, - preventDefault: () => {} - }; - jest.spyOn(event, 'preventDefault'); - jest.spyOn(controller.$.bankEntity, 'open'); + describe('onAccept()', () => { + it('should set the created bank entity id into the target account', () => { + controller.supplierAccounts = [{}, {}, {}]; - controller.showBankEntity(event); - - expect(event.preventDefault).not.toHaveBeenCalledWith(); - expect(controller.$.bankEntity.open).not.toHaveBeenCalledWith(); - }); - - it('should call preventDefault() and open() when the default is not prevented', () => { - const event = { - defaultPrevented: false, - preventDefault: () => {} + const data = { + id: 999, + index: 1 }; - jest.spyOn(event, 'preventDefault'); - jest.spyOn(controller.$.bankEntity, 'open'); + controller.onAccept(data); - controller.showBankEntity(event); + const targetAccount = controller.supplierAccounts[data.index]; - expect(event.preventDefault).toHaveBeenCalledWith(); - 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); + expect(targetAccount.bankEntityFk).toEqual(data.id); }); }); diff --git a/modules/ticket/front/main/index.html b/modules/ticket/front/main/index.html index 8e9af1e12..590d33887 100644 --- a/modules/ticket/front/main/index.html +++ b/modules/ticket/front/main/index.html @@ -2,7 +2,7 @@ vn-id="model" url="Tickets/filter" limit="20" - order="shippedDate DESC, shippedHour ASC, zoneLanding ASC"> + order="shippedDate DESC, shippedHour ASC, zoneLanding ASC, id"> Festive + + + + Current day +
diff --git a/modules/worker/front/calendar/locale/es.yml b/modules/worker/front/calendar/locale/es.yml index 1ff12358c..464ad9750 100644 --- a/modules/worker/front/calendar/locale/es.yml +++ b/modules/worker/front/calendar/locale/es.yml @@ -7,4 +7,5 @@ of: de days: días 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 -You can just add absences within the current year: Solo puedes añadir ausencias dentro del año actual \ No newline at end of file +You can just add absences within the current year: Solo puedes añadir ausencias dentro del año actual +Current day: Día actual \ No newline at end of file diff --git a/modules/worker/front/calendar/style.scss b/modules/worker/front/calendar/style.scss index b778a68b5..a028c8c30 100644 --- a/modules/worker/front/calendar/style.scss +++ b/modules/worker/front/calendar/style.scss @@ -41,12 +41,20 @@ vn-worker-calendar { border-color: rgba(0, 0, 0, 0.3); border-bottom: 1px solid rgba(0, 0, 0, 0.3); } - - .festive { - background-color:white; - border: 2px solid $color-alert; + + vn-avatar.festive, + vn-avatar.today { + background-color: $color-font-dark; width: 24px; min-width: 24px; height: 24px } + + vn-avatar.festive { + border: 2px solid $color-alert + } + + vn-avatar.today { + border: 2px solid $color-font-link + } } diff --git a/print/core/smtp.js b/print/core/smtp.js index c42a080f5..5fb5c4a2c 100644 --- a/print/core/smtp.js +++ b/print/core/smtp.js @@ -25,7 +25,7 @@ module.exports = { throw err; }).finally(async() => { 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, ?, ?, ?)`, [ options.to, options.replyTo, diff --git a/print/methods/closure.js b/print/methods/closure.js index 4c6f91b58..daa2d5e7c 100644 --- a/print/methods/closure.js +++ b/print/methods/closure.js @@ -143,8 +143,24 @@ module.exports = app => { AND t.refFk IS NULL GROUP BY e.ticketFk`, [reqArgs.routeId]); const ticketIds = tickets.map(ticket => ticket.id); - 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) { next(error); } diff --git a/print/templates/email/claim-pickup-order/locale/es.yml b/print/templates/email/claim-pickup-order/locale/es.yml index 1d49b2b2b..fe08fb0a8 100644 --- a/print/templates/email/claim-pickup-order/locale/es.yml +++ b/print/templates/email/claim-pickup-order/locale/es.yml @@ -2,22 +2,4 @@ subject: Orden de recogida title: Orden de recogida description: dear: Estimado cliente - 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. + instructions: Aqui tienes tu orden de recogida. \ No newline at end of file diff --git a/print/templates/reports/delivery-note/delivery-note.html b/print/templates/reports/delivery-note/delivery-note.html index 771f1fd0c..36d9abde1 100644 --- a/print/templates/reports/delivery-note/delivery-note.html +++ b/print/templates/reports/delivery-note/delivery-note.html @@ -118,20 +118,20 @@ -
+
-

{{$t('services')}}

+

{{$t('services.title')}}

- - - + + + - - + + @@ -148,25 +148,26 @@
{{$t('quantity')}}{{$t('concept')}}{{$t('price')}}{{$t('services.theader.quantity')}}{{$t('services.theader.concept')}}{{$t('services.theader.price')}} {{$t('vat')}}{{$t('amount')}}{{$t('services.theader.vat')}}{{$t('services.theader.amount')}}
- {{$t('subtotal')}} + {{$t('services.tfoot.subtotal')}} {{serviceTotal | currency('EUR', $i18n.locale)}}
+ * {{ $t('services.warning') }}
-

{{$t('packagings')}}

+

{{$t('packagings.title')}}

- - - + + + @@ -183,21 +184,18 @@
-
{{$t('reference')}}{{$t('quantity')}}{{$t('concept')}}{{$t('packagings.theader.reference')}}{{$t('packagings.theader.quantity')}}{{$t('packagings.theader.concept')}}
- + - - - - + + + + diff --git a/print/templates/reports/delivery-note/locale/en.yml b/print/templates/reports/delivery-note/locale/en.yml index ec961cabe..8a3ff834b 100644 --- a/print/templates/reports/delivery-note/locale/en.yml +++ b/print/templates/reports/delivery-note/locale/en.yml @@ -12,17 +12,37 @@ price: PSP/u discount: Disc. vat: VAT amount: Amount -type: Type -taxBase: Tax base -tax: Tax -fee: Fee total: Total subtotal: Subtotal -taxBreakdown: Tax breakdown -packagings: Buckets and packaging -services: Services vatType: VAT Type digitalSignature: Digital signature ticket: Delivery note {0} plantPassport: Plant passport -packages: Packages \ No newline at end of file +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 \ No newline at end of file diff --git a/print/templates/reports/delivery-note/locale/es.yml b/print/templates/reports/delivery-note/locale/es.yml index 674cb875e..f9c2e02f3 100644 --- a/print/templates/reports/delivery-note/locale/es.yml +++ b/print/templates/reports/delivery-note/locale/es.yml @@ -12,17 +12,37 @@ price: PVP/u discount: Dto. vat: IVA amount: Importe -type: Tipo -taxBase: Base imp. -tax: Tasa -fee: Cuota total: Total subtotal: Subtotal -taxBreakdown: Desglose impositivo -packagings: Cubos y embalajes -services: Servicios vatType: Tipo de IVA digitalSignature: Firma digital ticket: Albarán {0} plantPassport: Pasaporte fitosanitario -packages: Bultos \ No newline at end of file +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 \ No newline at end of file diff --git a/print/templates/reports/delivery-note/locale/fr.yml b/print/templates/reports/delivery-note/locale/fr.yml index 526df3068..72ca771e1 100644 --- a/print/templates/reports/delivery-note/locale/fr.yml +++ b/print/templates/reports/delivery-note/locale/fr.yml @@ -12,17 +12,37 @@ price: PRIX/u discount: Remise vat: TVA amount: Montant -type: Type -taxBase: Base imposable -tax: Taxe -fee: Quote total: Total subtotal: Total partiel -taxBreakdown: Répartition taxes -packagings: Bacs et emballages -services: Service vatType: Type de TVA digitalSignature: Signature numérique ticket: BL {0} plantPassport: Passeport phytosanitaire -packages: Paquets \ No newline at end of file +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 \ No newline at end of file diff --git a/print/templates/reports/delivery-note/locale/pt.yml b/print/templates/reports/delivery-note/locale/pt.yml index 95bcd857e..e83087142 100644 --- a/print/templates/reports/delivery-note/locale/pt.yml +++ b/print/templates/reports/delivery-note/locale/pt.yml @@ -12,17 +12,37 @@ price: PVP/u discount: Dto. vat: IVA amount: Importe -type: Tipo -taxBase: Base imp. -tax: Taxa -fee: Quota total: Total subtotal: Sub-total -taxBreakdown: Desglose impositivo -packagings: Baldes e Embalagens -services: Serviços vatType: Tipo de IVA digitalSignature: Assinatura digital ticket: Nota de Entrega {0} plantPassport: Passaporte vegetal -packages: Pacotes \ No newline at end of file +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 \ No newline at end of file
{{$t('taxBreakdown')}}{{$t('taxes.title')}}
{{$t('type')}} - {{$t('taxBase')}} - {{$t('tax')}}{{$t('fee')}}{{$t('taxes.theader.type')}}{{$t('taxes.theader.taxBase')}}{{$t('taxes.theader.tax')}}{{$t('taxes.theader.fee')}}