diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c97e65d6..3f241b91f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [Artículo](Datos Básicos) Añadido campo Unidades/Caja ### Changed +- [Tickets](Líneas preparadas) Actualizada sección para que sea más visual ### Fixed - [General] Al utilizar el traductor de Google se descuadraban los iconos diff --git a/db/changes/230201/00-ACL_ItemShelvingSale.sql b/db/changes/230201/00-ACL_ItemShelvingSale.sql new file mode 100644 index 000000000..38b65f89a --- /dev/null +++ b/db/changes/230201/00-ACL_ItemShelvingSale.sql @@ -0,0 +1,2 @@ +INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`) + VALUES ('ItemShelvingSale','*','*','ALLOW','employee'); diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 1ea4fa114..f1c9df51f 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -1143,10 +1143,10 @@ INSERT INTO `vn`.`itemShelving` (`itemFk`, `shelvingFk`, `visible`, `grouping`, INSERT INTO `vn`.`itemShelvingSale` (`itemShelvingFk`, `saleFk`, `quantity`, `created`, `userFk`) VALUES - ('1', '1', '1', '', '1106'), - ('2', '2', '5', '', '1106'), - ('1', '7', '1', '', '1106'), - ('2', '8', '5', '', '1106'); + ('1', '1', '1', util.VN_CURDATE(), '1106'), + ('2', '2', '5', util.VN_CURDATE(), '1106'), + ('1', '7', '1', util.VN_CURDATE(), '1106'), + ('2', '8', '5', util.VN_CURDATE(), '1106'); INSERT INTO `vncontrol`.`accion`(`accion_id`, `accion`) VALUES @@ -1779,7 +1779,7 @@ INSERT INTO `vn`.`claimDestination`(`id`, `description`, `addressFk`) INSERT INTO `vn`.`claimDevelopment`(`id`, `claimFk`, `claimResponsibleFk`, `workerFk`, `claimReasonFk`, `claimResultFk`, `claimRedeliveryFk`, `claimDestinationFk`) VALUES (1, 1, 1, 21, 1, 1, 2, 5), - (2, 1, 1, 21, 7, 2, 2, 5), + (2, 1, 2, 21, 7, 2, 2, 5), (3, 2, 7, 21, 9, 3, 2, 5), (4, 3, 7, 21, 15, 8, 2, 5), (5, 4, 7, 21, 7, 8, 2, 5); diff --git a/front/core/components/table/style.scss b/front/core/components/table/style.scss index d0a29a3ba..557268661 100644 --- a/front/core/components/table/style.scss +++ b/front/core/components/table/style.scss @@ -29,7 +29,7 @@ vn-table { & > tbody { display: table-row-group; } - & > vn-tfoot, + & > vn-tfoot, & > .vn-tfoot, & > tfoot { border-top: $border; @@ -42,7 +42,7 @@ vn-table { height: 48px; } vn-thead, .vn-thead, - vn-tbody, .vn-tbody, + vn-tbody, .vn-tbody, vn-tfoot, .vn-tfoot, thead, tbody, tfoot { & > * { @@ -153,6 +153,18 @@ vn-table { background-color: $color-font-bg-dark; color: $color-font-bg; } + &.dark-notice { + background-color: $color-notice; + color: $color-font-bg; + } + &.yellow { + background-color: $color-yellow; + color: $color-font-bg; + } + &.pink { + background-color: $color-pink; + color: $color-font-bg; + } } vn-icon-menu { display: inline-block; @@ -194,7 +206,7 @@ vn-table.scrollable > .vn-table, } vn-thead th, - vn-thead vn-th, + vn-thead vn-th, thead vn-th, thead th { border-bottom: $border; @@ -217,4 +229,4 @@ vn-table.scrollable.lg, .tableWrapper { overflow-x: auto; -} \ No newline at end of file +} diff --git a/front/core/styles/variables.scss b/front/core/styles/variables.scss index c79a8590f..bcc9fab66 100644 --- a/front/core/styles/variables.scss +++ b/front/core/styles/variables.scss @@ -101,6 +101,8 @@ $color-marginal: #222; $color-success: #a3d131; $color-notice: #32b1ce; $color-alert: #fa3939; +$color-pink: #ff99cc; +$color-yellow: #ffff00; $color-button: $color-secondary; $color-spacer: rgba(255, 255, 255, .3); diff --git a/modules/claim/back/methods/claim/filter.js b/modules/claim/back/methods/claim/filter.js index e86830200..d653229e5 100644 --- a/modules/claim/back/methods/claim/filter.js +++ b/modules/claim/back/methods/claim/filter.js @@ -23,7 +23,7 @@ module.exports = Self => { { arg: 'search', type: 'string', - description: `If it's and integer searchs by id, otherwise it searchs by client name`, + description: `If it's a number searchs by id, otherwise it searchs by client name`, http: {source: 'query'} }, { @@ -34,31 +34,31 @@ module.exports = Self => { }, { arg: 'id', - type: 'integer', + type: 'number', description: 'The claim id', http: {source: 'query'} }, { arg: 'clientFk', - type: 'integer', + type: 'number', description: 'The client id', http: {source: 'query'} }, { arg: 'claimStateFk', - type: 'integer', + type: 'number', description: 'The claim state id', http: {source: 'query'} }, { arg: 'salesPersonFk', - type: 'integer', + type: 'number', description: 'The salesPerson id', http: {source: 'query'} }, { arg: 'attenderFk', - type: 'integer', + type: 'number', description: 'The attender worker id', http: {source: 'query'} }, @@ -67,6 +67,18 @@ module.exports = Self => { type: 'date', description: 'The to date filter', http: {source: 'query'} + }, + { + arg: 'itemFk', + type: 'number', + description: 'The item id', + http: {source: 'query'} + }, + { + arg: 'claimResponsibleFk', + type: 'number', + description: 'The claimResponsible id', + http: {source: 'query'} } ], returns: { @@ -80,33 +92,58 @@ module.exports = Self => { }); Self.filter = async(ctx, filter, options) => { + const models = Self.app.models; const conn = Self.dataSource.connector; + const args = ctx.args; const myOptions = {}; let to; if (typeof options == 'object') Object.assign(myOptions, options); - const where = buildFilter(ctx.args, (param, value) => { + let claimIdsByItemFk = []; + let claimIdsByClaimResponsibleFk = []; + + if (args.itemFk) { + query = `SELECT cb.claimFk + FROM claimBeginning cb + LEFT JOIN sale s ON s.id = cb.saleFk + WHERE s.itemFk = ?`; + const claims = await Self.rawSql(query, [args.itemFk], myOptions); + claimIdsByItemFk = claims.map(claim => claim.claimFk); + } + + if (args.claimResponsibleFk) { + const claims = await models.ClaimDevelopment.find({ + fields: ['claimFk'], + where: {claimResponsibleFk: args.claimResponsibleFk} + }, myOptions); + claimIdsByClaimResponsibleFk = claims.map(claim => claim.claimFk); + } + + const where = buildFilter(args, (param, value) => { switch (param) { case 'search': return /^\d+$/.test(value) ? {'cl.id': value} : { or: [ - {'cl.clientName': {like: `%${value}%`}} + {'c.name': {like: `%${value}%`}} ] }; case 'clientName': - return {'cl.clientName': {like: `%${value}%`}}; + return {'c.name': {like: `%${value}%`}}; case 'clientFk': - return {'cl.clientFk': value}; case 'id': case 'claimStateFk': case 'priority': return {[`cl.${param}`]: value}; + case 'itemFk': + return {'cl.id': {inq: claimIdsByItemFk}}; + case 'claimResponsibleFk': + return {'cl.id': {inq: claimIdsByClaimResponsibleFk}}; case 'salesPersonFk': - return {'cl.salesPersonFk': value}; + return {'c.salesPersonFk': value}; case 'attenderFk': return {'cl.workerFk': value}; case 'created': @@ -118,29 +155,23 @@ module.exports = Self => { } }); - filter = mergeFilters(ctx.args.filter, {where}); + filter = mergeFilters(args.filter, {where}); const stmts = []; const stmt = new ParameterizedSQL( - `SELECT * - FROM ( - SELECT - cl.id, - cl.clientFk, - c.name AS clientName, - cl.workerFk, - u.name AS workerName, - cs.description, - cl.created, - cs.priority, - cl.claimStateFk, - c.salesPersonFk - FROM claim cl - LEFT JOIN client c ON c.id = cl.clientFk - LEFT JOIN worker w ON w.id = cl.workerFk - LEFT JOIN account.user u ON u.id = w.userFk - LEFT JOIN claimState cs ON cs.id = cl.claimStateFk ) cl` + `SELECT + cl.id, + cl.clientFk, + c.name AS clientName, + cl.workerFk, + u.name AS workerName, + cs.description, + cl.created + FROM claim cl + LEFT JOIN client c ON c.id = cl.clientFk + LEFT JOIN account.user u ON u.id = cl.workerFk + LEFT JOIN claimState cs ON cs.id = cl.claimStateFk` ); stmt.merge(conn.makeSuffix(filter)); diff --git a/modules/claim/back/methods/claim/specs/filter.spec.js b/modules/claim/back/methods/claim/specs/filter.spec.js index b26afe8c4..49e258505 100644 --- a/modules/claim/back/methods/claim/specs/filter.spec.js +++ b/modules/claim/back/methods/claim/specs/filter.spec.js @@ -57,4 +57,44 @@ describe('claim filter()', () => { throw e; } }); + + it('should return 3 results filtering by item id', async() => { + const tx = await app.models.Claim.beginTransaction({}); + + try { + const options = {transaction: tx}; + + const result = await app.models.Claim.filter({args: {filter: {}, itemFk: 2}}, null, options); + + expect(result.length).toEqual(3); + expect(result[0].id).toEqual(1); + expect(result[1].id).toEqual(2); + expect(result[2].id).toEqual(4); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should return 3 results filtering by claimResponsible id', async() => { + const tx = await app.models.Claim.beginTransaction({}); + + try { + const options = {transaction: tx}; + + const result = await app.models.Claim.filter({args: {filter: {}, claimResponsibleFk: 7}}, null, options); + + expect(result.length).toEqual(3); + expect(result[0].id).toEqual(2); + expect(result[1].id).toEqual(3); + expect(result[2].id).toEqual(4); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); }); diff --git a/modules/claim/front/search-panel/index.html b/modules/claim/front/search-panel/index.html index eec8cd727..151a06c8e 100644 --- a/modules/claim/front/search-panel/index.html +++ b/modules/claim/front/search-panel/index.html @@ -58,8 +58,28 @@ ng-model="filter.created"> + + + {{::id}} - {{::name}} + + + + - \ No newline at end of file + diff --git a/modules/claim/front/search-panel/index.js b/modules/claim/front/search-panel/index.js index a7e8fb046..2400b8ede 100644 --- a/modules/claim/front/search-panel/index.js +++ b/modules/claim/front/search-panel/index.js @@ -1,7 +1,14 @@ import ngModule from '../module'; import SearchPanel from 'core/components/searchbar/search-panel'; +class Controller extends SearchPanel { + itemSearchFunc($search) { + return /^\d+$/.test($search) + ? {id: $search} + : {name: {like: '%' + $search + '%'}}; + } +} ngModule.vnComponent('vnClaimSearchPanel', { template: require('./index.html'), - controller: SearchPanel + controller: Controller }); diff --git a/modules/item/back/methods/item-shelving-sale/filter.js b/modules/item/back/methods/item-shelving-sale/filter.js new file mode 100644 index 000000000..12029d33d --- /dev/null +++ b/modules/item/back/methods/item-shelving-sale/filter.js @@ -0,0 +1,49 @@ + +const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; + +module.exports = Self => { + Self.remoteMethod('filter', { + description: 'Returns all item shelving sale matching with the filter', + accessType: 'READ', + accepts: [{ + arg: 'filter', + type: 'object', + description: 'Filter defining where and paginated data' + }], + returns: { + type: ['object'], + root: true + }, + http: { + path: `/filter`, + verb: 'GET' + } + }); + + Self.filter = async(filter, options) => { + const conn = Self.dataSource.connector; + const myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + const stmt = new ParameterizedSQL(` + SELECT iss.created, + iss.saleFk, + iss.quantity, + iss.userFk, + ish.shelvingFk, + p.code, + u.name + FROM itemShelvingSale iss + LEFT JOIN itemShelving ish ON iss.itemShelvingFk = ish.id + LEFT JOIN shelving s ON ish.shelvingFk = s.code + LEFT JOIN parking p ON s.parkingFk = p.id + LEFT JOIN account.user u ON u.id = iss.userFk` + ); + + stmt.merge(conn.makeSuffix(filter)); + + return conn.executeStmt(stmt); + }; +}; diff --git a/modules/item/back/models/item-shelving-sale.js b/modules/item/back/models/item-shelving-sale.js new file mode 100644 index 000000000..b89be9f00 --- /dev/null +++ b/modules/item/back/models/item-shelving-sale.js @@ -0,0 +1,3 @@ +module.exports = Self => { + require('../methods/item-shelving-sale/filter')(Self); +}; diff --git a/modules/item/back/models/item-shelving.json b/modules/item/back/models/item-shelving.json index 951a4553a..0890350da 100644 --- a/modules/item/back/models/item-shelving.json +++ b/modules/item/back/models/item-shelving.json @@ -12,21 +12,12 @@ "id": true, "description": "Identifier" }, - "shelve": { - "type": "string" - }, "shelvingFk": { "type": "string" }, "itemFk": { "type": "number" }, - "deep": { - "type": "number" - }, - "quantity": { - "type": "number" - }, "created": { "type": "date" } @@ -41,6 +32,11 @@ "type": "belongsTo", "model": "Account", "foreignKey": "userFk" - } + }, + "shelving": { + "type": "belongsTo", + "model": "Shelving", + "foreignKey": "shelvingFk" + } } } diff --git a/modules/ticket/back/methods/sale/salePreparingList.js b/modules/ticket/back/methods/sale/salePreparingList.js new file mode 100644 index 000000000..e6e7d5164 --- /dev/null +++ b/modules/ticket/back/methods/sale/salePreparingList.js @@ -0,0 +1,33 @@ +module.exports = Self => { + Self.remoteMethodCtx('salePreparingList', { + description: 'Returns a list with the lines of a ticket and its different states of preparation', + accessType: 'READ', + accepts: [{ + arg: 'id', + type: 'number', + required: true, + description: 'The ticket id', + http: {source: 'path'} + }], + returns: { + type: ['object'], + root: true + }, + http: { + path: `/:id/salePreparingList`, + verb: 'GET' + } + }); + + Self.salePreparingList = async(ctx, id, options) => { + const myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + query = `CALL vn.salePreparingList(?)`; + const [sales] = await Self.rawSql(query, [id], myOptions); + + return sales; + }; +}; diff --git a/modules/ticket/back/models/sale.js b/modules/ticket/back/models/sale.js index ae247fc24..bab201fdd 100644 --- a/modules/ticket/back/models/sale.js +++ b/modules/ticket/back/models/sale.js @@ -1,5 +1,6 @@ module.exports = Self => { require('../methods/sale/getClaimableFromTicket')(Self); + require('../methods/sale/salePreparingList')(Self); require('../methods/sale/reserve')(Self); require('../methods/sale/deleteSales')(Self); require('../methods/sale/updatePrice')(Self); diff --git a/modules/ticket/front/sale-tracking/index.html b/modules/ticket/front/sale-tracking/index.html index fc585650a..851817551 100644 --- a/modules/ticket/front/sale-tracking/index.html +++ b/modules/ticket/front/sale-tracking/index.html @@ -1,10 +1,11 @@ @@ -12,31 +13,27 @@ - + Is checked Item - Description + Description Quantity - Original - Worker - State - Created + - - - - + + + + + + + - {{sale.itemFk | zeroFill:6}} + {{::sale.itemFk | zeroFill:6}} @@ -53,16 +50,18 @@ {{::sale.quantity}} - {{::sale.originalQuantity}} - - - {{::sale.userNickname | dashIfEmpty}} - + + + + + - {{::sale.state}} - {{::sale.created | date: 'dd/MM/yyyy HH:mm'}} @@ -70,8 +69,99 @@ + warehouse-fk="$ctrl.ticket.warehouseFk" + ticket-fk="$ctrl.ticket.id"> - - \ No newline at end of file + + + + + + + + + + Quantity + Original + Worker + State + Created + + + + + {{::sale.quantity}} + {{::sale.originalQuantity}} + + + {{::sale.userNickname | dashIfEmpty}} + + + {{::sale.state}} + {{::sale.created | date: 'dd/MM/yyyy HH:mm'}} + + + + + + + + + + + + + + + + + + + + Quantity + Worker + Shelving + Parking + Created + + + + + {{::itemShelvingSale.quantity}} + + + {{::itemShelvingSale.name | dashIfEmpty}} + + + {{::itemShelvingSale.shelvingFk}} + {{::itemShelvingSale.code}} + {{::itemShelvingSale.created | date: 'dd/MM/yyyy HH:mm'}} + + + + + + + + diff --git a/modules/ticket/front/sale-tracking/index.js b/modules/ticket/front/sale-tracking/index.js index 394ef4f1e..60e42a461 100644 --- a/modules/ticket/front/sale-tracking/index.js +++ b/modules/ticket/front/sale-tracking/index.js @@ -1,12 +1,100 @@ import ngModule from '../module'; import Section from 'salix/components/section'; +import './style.scss'; -class Controller extends Section {} +class Controller extends Section { + constructor($element, $) { + super($element, $); + this.filter = { + include: [ + { + relation: 'item' + }, { + relation: 'saleTracking', + scope: { + fields: ['isChecked'] + } + } + ] + }; + } + + get sales() { + return this._sales; + } + + set sales(value) { + this._sales = value; + if (value) { + const query = `Sales/${this.$params.id}/salePreparingList`; + this.$http.get(query) + .then(res => { + this.salePreparingList = res.data; + for (const salePreparing of this.salePreparingList) { + for (const sale of this.sales) { + if (salePreparing.saleFk == sale.id) + sale.preparingList = salePreparing; + } + } + }); + } + } + + showItemDescriptor(event, sale) { + this.quicklinks = { + btnThree: { + icon: 'icon-transaction', + state: `item.card.diary({ + id: ${sale.itemFk}, + warehouseFk: ${this.ticket.warehouseFk}, + lineFk: ${sale.id} + })`, + tooltip: 'Item diary' + } + }; + this.$.itemDescriptor.show(event.target, sale.itemFk); + } + + chipHasSaleGroupDetail(hasSaleGroupDetail) { + if (hasSaleGroupDetail) return 'pink'; + else return 'message'; + } + + chipIsPreviousSelected(isPreviousSelected) { + if (isPreviousSelected) return 'notice'; + else return 'message'; + } + + chipIsPrevious(isPrevious) { + if (isPrevious) return 'dark-notice'; + else return 'message'; + } + + chipIsPrepared(isPrepared) { + if (isPrepared) return 'warning'; + else return 'message'; + } + + chipIsControled(isControled) { + if (isControled) return 'yellow'; + else return 'message'; + } + + showSaleTracking(sale) { + this.saleId = sale.id; + this.$.saleTracking.show(); + } + + showItemShelvingSale(sale) { + this.saleId = sale.id; + this.$.itemShelvingSale.show(); + } +} ngModule.vnComponent('vnTicketSaleTracking', { template: require('./index.html'), controller: Controller, bindings: { - ticket: '<', - }, + ticket: '<' + } }); diff --git a/modules/ticket/front/sale-tracking/locale/es.yml b/modules/ticket/front/sale-tracking/locale/es.yml new file mode 100644 index 000000000..eabc0a04d --- /dev/null +++ b/modules/ticket/front/sale-tracking/locale/es.yml @@ -0,0 +1,6 @@ +ItemShelvings sale: Carros línea +has saleGroupDetail: tiene detalle grupo lineas +is previousSelected: es previa seleccionada +is previous: es previa +is prepared: esta preparado +is controled: esta controlado diff --git a/modules/ticket/front/sale-tracking/style.scss b/modules/ticket/front/sale-tracking/style.scss new file mode 100644 index 000000000..6d8b3db69 --- /dev/null +++ b/modules/ticket/front/sale-tracking/style.scss @@ -0,0 +1,7 @@ +@import "variables"; + +.chip { + display: inline-block; + min-width: 15px; + min-height: 25px; +}