diff --git a/db/changes/10340-summer/00-buy_importReference.sql b/db/changes/10340-summer/00-buy_importReference.sql new file mode 100644 index 000000000..f6bdc059c --- /dev/null +++ b/db/changes/10340-summer/00-buy_importReference.sql @@ -0,0 +1,14 @@ +create table `vn`.`itemMatchProperties` +( + itemFk int not null, + name varchar(80) not null, + producer varchar(80) not null, + size int not null, + constraint itemMatchProperties_pk + primary key (itemFk, name, producer, size), + constraint itemFk___fk + foreign key (itemFk) references item (id) + on update cascade on delete cascade +) +comment 'Propiedades para encontrar articulos equivalentes en verdnatura'; + diff --git a/e2e/paths/12-entry/07_buys.spec.js b/e2e/paths/12-entry/07_buys.spec.js index e5617b8bd..4042c99b6 100644 --- a/e2e/paths/12-entry/07_buys.spec.js +++ b/e2e/paths/12-entry/07_buys.spec.js @@ -29,9 +29,6 @@ describe('Entry import, create and edit buys path', () => { }); it('should fill the form, import the designated JSON file and select items for each import and confirm import', async() => { - await page.write(selectors.entryBuys.ref, 'a reference'); - await page.write(selectors.entryBuys.observation, 'an observation'); - let currentDir = process.cwd(); let filePath = `${currentDir}/e2e/assets/07_import_buys.json`; @@ -41,6 +38,9 @@ describe('Entry import, create and edit buys path', () => { ]); await fileChooser.accept([filePath]); + await page.waitForTextInField(selectors.entryBuys.ref, '200573095, 200573106, 200573117, 200573506'); + await page.waitForTextInField(selectors.entryBuys.observation, '729-6340 2846'); + await page.autocompleteSearch(selectors.entryBuys.firstImportedItem, 'Ranged Reinforced weapon pistol 9mm'); await page.autocompleteSearch(selectors.entryBuys.secondImportedItem, 'Melee Reinforced weapon heavy shield 1x0.5m'); await page.autocompleteSearch(selectors.entryBuys.thirdImportedItem, 'Container medical box 1m'); diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 2484990ed..d4de6e55b 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -57,7 +57,14 @@ "The postcode doesn't exist. Please enter a correct one": "The postcode doesn't exist. Please enter a correct one", "Can't create stowaway for this ticket": "Can't create stowaway for this ticket", "Swift / BIC can't be empty": "Swift / BIC can't be empty", - "Bought units from buy request": "Bought {{quantity}} units of {{concept}} [{{itemId}}]({{{urlItem}}}) for the ticket id [{{ticketId}}]({{{url}}})", + "Deleted sales from ticket": "I have deleted the following lines from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}", + "Added sale to ticket": "I have added the following line to the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}", + "Changed sale discount": "I have changed the following lines discounts from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Created claim": "I have created the claim [{{claimId}}]({{{claimUrl}}}) for the following lines from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Changed sale price": "I have changed the price of [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) from {{oldPrice}}€ ➔ *{{newPrice}}€* of the ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changed sale quantity": "I have changed the quantity of [{{itemId}} {{concept}}]({{{itemUrl}}}) from {{oldQuantity}} ➔ *{{newQuantity}}* of the ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changed sale reserved state": "I have changed the following lines reserved state from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Bought units from buy request": "Bought {{quantity}} units of [{{itemId}} {{concept}}]({{{urlItem}}}) for the ticket id [{{ticketId}}]({{{url}}})", "MESSAGE_INSURANCE_CHANGE": "I have changed the insurence credit of client [{{clientName}} ({{clientId}})]({{{url}}}) to *{{credit}} €*", "Changed client paymethod": "I have changed the pay method for client [{{clientName}} ({{clientId}})]({{{url}}})", "Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} ({{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [{{ticketId}}]({{{ticketUrl}}})", @@ -99,5 +106,9 @@ "None": "None", "error densidad = 0": "error densidad = 0", "This document already exists on this ticket": "This document already exists on this ticket", - "serial non editable": "This serial doesn't allow to set a reference" + "serial non editable": "This serial doesn't allow to set a reference", + "nickname": "nickname", + "State": "State", + "regular": "regular", + "reserved": "reserved" } \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 12003cf08..92cd8d343 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -54,7 +54,7 @@ "This address doesn't exist": "Este consignatario no existe", "You can't create an order for a inactive client": "You can't create an order for a inactive client", "You can't create an order for a client that doesn't has tax data verified": "You can't create an order for a client that doesn't has tax data verified", - "You must delete the claim id %d first": "Antes debes borrar la reclamacion %d", + "You must delete the claim id %d first": "Antes debes borrar la reclamación %d", "You don't have enough privileges": "No tienes suficientes permisos", "Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF", "You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos basicos de una orden con artículos", @@ -122,7 +122,17 @@ "Swift / BIC can't be empty": "Swift / BIC no puede estar vacío", "Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios", "Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios", - "Bought units from buy request": "Se ha comprado {{quantity}} unidades de {{concept}} [{{itemId}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})", + "Deleted sales from ticket": "He eliminado las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}", + "Added sale to ticket": "He añadido la siguiente linea al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}", + "Changed sale discount": "He cambiado el descuento de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Created claim": "He creado la reclamación [{{claimId}}]({{{claimUrl}}}) de las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Changed sale price": "He cambiado el precio de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* del ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changed sale quantity": "He cambiado la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}* del ticket [{{ticketId}}]({{{ticketUrl}}})", + "State": "Estado", + "regular": "normal", + "reserved": "reservado", + "Changed sale reserved state": "He cambiado el estado reservado de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Bought units from buy request": "Se ha comprado {{quantity}} unidades de [{{itemId}} {{concept}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})", "MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*", "Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})", "Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})", diff --git a/modules/claim/back/methods/claim/createFromSales.js b/modules/claim/back/methods/claim/createFromSales.js index 2dd1b75c2..f22aabbf3 100644 --- a/modules/claim/back/methods/claim/createFromSales.js +++ b/modules/claim/back/methods/claim/createFromSales.js @@ -3,17 +3,20 @@ const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { Self.remoteMethodCtx('createFromSales', { description: 'Create a claim', - accepts: [{ - arg: 'ticketId', - type: 'Number', - required: true, - description: 'The origin ticket id' - }, { - arg: 'sales', - type: ['Object'], - required: true, - description: 'The claimed sales' - }], + accepts: [ + { + arg: 'ticketId', + type: 'number', + required: true, + description: 'The origin ticket id' + }, + { + arg: 'sales', + type: ['object'], + required: true, + description: 'The claimed sales' + } + ], returns: { type: 'object', root: true @@ -25,6 +28,7 @@ module.exports = Self => { }); Self.createFromSales = async(ctx, ticketId, sales, options) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; let tx; const myOptions = {}; @@ -39,7 +43,20 @@ module.exports = Self => { const userId = ctx.req.accessToken.userId; try { - const ticket = await models.Ticket.findById(ticketId, null, myOptions); + const ticket = await models.Ticket.findById(ticketId, { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + }, myOptions); + if (ticket.isDeleted) throw new UserError(`You can't create a claim for a removed ticket`); @@ -49,6 +66,8 @@ module.exports = Self => { ticketCreated: ticket.shipped, workerFk: userId }, myOptions); + + let changesMade = ''; const promises = []; for (const sale of sales) { @@ -59,10 +78,25 @@ module.exports = Self => { }, myOptions); promises.push(newClaimBeginning); + changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`; } await Promise.all(promises); + const salesPerson = ticket.client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + + const message = $t('Created claim', { + claimId: newClaim.id, + ticketId: ticketId, + ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`, + claimUrl: `${origin}/#!/claim/${newClaim.id}/summary`, + changes: changesMade + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + if (tx) await tx.commit(); return newClaim; diff --git a/modules/claim/back/methods/claim/specs/createFromSales.spec.js b/modules/claim/back/methods/claim/specs/createFromSales.spec.js index f08914025..849ccf8f5 100644 --- a/modules/claim/back/methods/claim/specs/createFromSales.spec.js +++ b/modules/claim/back/methods/claim/specs/createFromSales.spec.js @@ -7,7 +7,13 @@ describe('Claim createFromSales()', () => { instance: 0, quantity: 10 }]; - const ctx = {req: {accessToken: {userId: 1}}}; + const ctx = { + req: { + accessToken: {userId: 1}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; it('should create a new claim', async() => { const tx = await app.models.Claim.beginTransaction({}); diff --git a/modules/entry/back/methods/entry/importBuys.js b/modules/entry/back/methods/entry/importBuys.js index 325fe4d22..3ed8ac1c7 100644 --- a/modules/entry/back/methods/entry/importBuys.js +++ b/modules/entry/back/methods/entry/importBuys.js @@ -11,11 +11,6 @@ module.exports = Self => { description: 'The entry id', http: {source: 'path'} }, - { - arg: 'options', - type: 'object', - description: 'Callback options', - }, { arg: 'ref', type: 'string', @@ -28,11 +23,11 @@ module.exports = Self => { }, { arg: 'buys', - type: ['Object'], + type: ['object'], description: 'The buys', }], returns: { - type: ['Object'], + type: ['object'], root: true }, http: { @@ -41,23 +36,27 @@ module.exports = Self => { } }); - Self.importBuys = async(ctx, id, options = {}) => { + Self.importBuys = async(ctx, id, options) => { const conn = Self.dataSource.connector; const args = ctx.args; const models = Self.app.models; let tx; + const myOptions = {}; - if (!options.transaction) { + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { tx = await Self.beginTransaction({}); - options.transaction = tx; + myOptions.transaction = tx; } try { - const entry = await models.Entry.findById(id, null, options); + const entry = await models.Entry.findById(id, null, myOptions); await entry.updateAttributes({ observation: args.observation, ref: args.ref - }, options); + }, myOptions); const buys = []; for (let buy of args.buys) { @@ -71,9 +70,16 @@ module.exports = Self => { buyingValue: buy.buyingValue, packageFk: buy.packageFk }); + + await models.ItemMatchProperties.upsert({ + itemFk: buy.itemFk, + name: buy.description, + producer: buy.companyName, + size: buy.size + }, myOptions); } - const createdBuys = await models.Buy.create(buys, options); + const createdBuys = await models.Buy.create(buys, myOptions); const buyIds = createdBuys.map(buy => buy.id); const stmts = []; @@ -90,7 +96,7 @@ module.exports = Self => { stmts.push('CALL buy_recalcPrices()'); const sql = ParameterizedSQL.join(stmts, ';'); - await conn.executeStmt(sql, options); + await conn.executeStmt(sql, myOptions); if (tx) await tx.commit(); } catch (e) { if (tx) await tx.rollback(); diff --git a/modules/entry/back/methods/entry/importBuysPreview.js b/modules/entry/back/methods/entry/importBuysPreview.js index 9ba2b58ed..790d33364 100644 --- a/modules/entry/back/methods/entry/importBuysPreview.js +++ b/modules/entry/back/methods/entry/importBuysPreview.js @@ -37,7 +37,21 @@ module.exports = Self => { where: {volume: {gte: buy.volume}}, order: 'volume ASC' }, myOptions); - buy.packageFk = packaging.id; + + if (packaging) + buy.packageFk = packaging.id; + + const reference = await models.ItemMatchProperties.findOne({ + fields: ['itemFk'], + where: { + name: buy.description, + producer: buy.companyName, + size: buy.size + } + }, myOptions); + + if (reference) + buy.itemFk = reference.itemFk; } return buys; diff --git a/modules/entry/back/model-config.json b/modules/entry/back/model-config.json index eddef9c41..ad5a9063e 100644 --- a/modules/entry/back/model-config.json +++ b/modules/entry/back/model-config.json @@ -5,6 +5,9 @@ "Buy": { "dataSource": "vn" }, + "ItemMatchProperties": { + "dataSource": "vn" + }, "EntryLog": { "dataSource": "vn" }, diff --git a/modules/entry/back/models/buy-import-reference.json b/modules/entry/back/models/buy-import-reference.json new file mode 100644 index 000000000..ab64dad73 --- /dev/null +++ b/modules/entry/back/models/buy-import-reference.json @@ -0,0 +1,32 @@ +{ + "name": "ItemMatchProperties", + "base": "VnModel", + "options": { + "mysql": { + "table": "itemMatchProperties" + } + }, + "properties": { + "itemFk": { + "type": "number", + "id": true, + "description": "Identifier" + }, + "name": { + "type": "string" + }, + "producer": { + "type": "string" + }, + "size": { + "type": "string" + } + }, + "relations": { + "item": { + "type": "belongsTo", + "model": "Item", + "foreignKey": "itemFk" + } + } +} \ No newline at end of file diff --git a/modules/entry/front/buy/import/index.html b/modules/entry/front/buy/import/index.html index 74b6c708a..179657dae 100644 --- a/modules/entry/front/buy/import/index.html +++ b/modules/entry/front/buy/import/index.html @@ -9,20 +9,6 @@ class="vn-ma-md">
- - - - - - - - + + + + + + + + @@ -51,7 +51,6 @@ - @@ -70,20 +69,19 @@ {{::id}} - {{::name}} + + + + - - + -
Grouping Buying value BoxVolume
{{::buy.description | dashIfEmpty}} {{::buy.size | dashIfEmpty}} - - {{::buy.packing | dashIfEmpty}} - - - - {{::buy.grouping | dashIfEmpty}} - - + {{::buy.packing | dashIfEmpty}}{{::buy.grouping | dashIfEmpty}} {{::buy.buyingValue | currency: 'EUR':2}} {{::buy.volume | number}}
@@ -110,7 +107,95 @@ label="Cancel" ui-sref="entry.card.buy.index"> -
+ + + + + + + + + + + + + + + + + + + + + + + + + + ID + Item + Size + Producer + Color + + + + + + + {{::item.id}} + + + {{::item.name}} + {{::item.size}} + {{::item.producer.name}} + {{::item.ink.name}} + + + + + + + + \ No newline at end of file diff --git a/modules/entry/front/buy/import/index.js b/modules/entry/front/buy/import/index.js index b5ff92a89..2f13b2746 100644 --- a/modules/entry/front/buy/import/index.js +++ b/modules/entry/front/buy/import/index.js @@ -29,6 +29,7 @@ class Controller extends Section { this.$.$applyAsync(() => { this.import.observation = invoice.tx_awb; + const companyName = invoice.tx_company; const boxes = invoice.boxes; const buys = []; for (let box of boxes) { @@ -37,11 +38,12 @@ class Controller extends Section { const packing = product.nu_stems_bunch * product.nu_bunches; buys.push({ description: product.nm_product, + companyName: companyName, size: product.nu_length, packing: packing, grouping: product.nu_stems_bunch, buyingValue: parseFloat(product.mny_rate_stem), - volume: boxVolume + volume: boxVolume, }); } } @@ -86,6 +88,59 @@ class Controller extends Section { ? {id: $search} : {name: {like: '%' + $search + '%'}}; } + + showFilterDialog(buy) { + this.activeBuy = buy; + this.itemFilterParams = {}; + this.itemFilter = { + include: [ + { + relation: 'producer', + scope: { + fields: ['name'] + } + }, + { + relation: 'ink', + scope: { + fields: ['name'] + } + } + ] + }; + + this.$.filterDialog.show(); + } + + selectItem(id) { + this.activeBuy['itemFk'] = id; + this.$.filterDialog.hide(); + } + + filter() { + const filter = this.itemFilter; + const params = this.itemFilterParams; + const where = {}; + + for (let key in params) { + const value = params[key]; + if (!value) continue; + + switch (key) { + case 'name': + where[key] = {like: `%${value}%`}; + break; + case 'producerFk': + case 'typeFk': + case 'size': + case 'ink': + where[key] = value; + } + } + + filter.where = where; + this.$.itemsModel.applyFilter(filter); + } } Controller.$inject = ['$element', '$scope']; diff --git a/modules/entry/front/buy/import/style.scss b/modules/entry/front/buy/import/style.scss index dba069616..8426d4169 100644 --- a/modules/entry/front/buy/import/style.scss +++ b/modules/entry/front/buy/import/style.scss @@ -2,4 +2,10 @@ vn-entry-buy-import { .vn-table > tbody td:nth-child(1) { width: 250px } +} + +.itemFilter { + vn-table.scrollable { + height: 500px + } } \ No newline at end of file diff --git a/modules/entry/front/buy/locale/es.yml b/modules/entry/front/buy/locale/es.yml index c77587758..55828a3c6 100644 --- a/modules/entry/front/buy/locale/es.yml +++ b/modules/entry/front/buy/locale/es.yml @@ -3,4 +3,6 @@ Observation: Observación Box: Embalaje Import buys: Importar compras Some of the imported buys doesn't have an item: Algunas de las compras importadas no tienen un artículo -JSON files only: Solo ficheros JSON \ No newline at end of file +JSON files only: Solo ficheros JSON +Filter item: Filtrar artículo +Filter...: Filtrar... \ No newline at end of file diff --git a/modules/invoiceOut/front/summary/index.html b/modules/invoiceOut/front/summary/index.html index 0406a39c5..924972299 100644 --- a/modules/invoiceOut/front/summary/index.html +++ b/modules/invoiceOut/front/summary/index.html @@ -1,6 +1,7 @@ diff --git a/modules/invoiceOut/front/summary/index.js b/modules/invoiceOut/front/summary/index.js index 2446e986a..af0b5365e 100644 --- a/modules/invoiceOut/front/summary/index.js +++ b/modules/invoiceOut/front/summary/index.js @@ -7,7 +7,7 @@ class Controller extends Summary { this._invoiceOut = value; if (value && value.id) { this.getSummary(); - this.getTickets(); + this.$.ticketsModel.url = `InvoiceOuts/${this.invoiceOut.id}/getTickets`; } } @@ -19,14 +19,6 @@ class Controller extends Summary { return this.$http.get(`InvoiceOuts/${this.invoiceOut.id}/summary`) .then(res => this.summary = res.data); } - - getTickets() { - this.$.$applyAsync(() => { - const query = `InvoiceOuts/${this.invoiceOut.id}/getTickets`; - this.$.ticketsModel.url = query; - this.$.ticketsModel.refresh(); - }); - } } ngModule.vnComponent('vnInvoiceOutSummary', { diff --git a/modules/invoiceOut/front/summary/index.spec.js b/modules/invoiceOut/front/summary/index.spec.js index be6ad0a18..675cc02c3 100644 --- a/modules/invoiceOut/front/summary/index.spec.js +++ b/modules/invoiceOut/front/summary/index.spec.js @@ -27,17 +27,5 @@ describe('InvoiceOut', () => { expect(controller.summary).toEqual('the data you are looking for'); }); }); - - describe('getTickets()', () => { - it('should perform a and then call to the ticketModel refresh() method', () => { - jest.spyOn(controller.$.ticketsModel, 'refresh'); - - controller.getTickets(); - $scope.$apply(); - - expect(controller.$.ticketsModel.url).toEqual('InvoiceOuts/1/getTickets'); - expect(controller.$.ticketsModel.refresh).toHaveBeenCalledWith(); - }); - }); }); }); diff --git a/modules/item/front/search-panel/locale/es.yml b/modules/item/front/search-panel/locale/es.yml index 197da0695..67a5200d7 100644 --- a/modules/item/front/search-panel/locale/es.yml +++ b/modules/item/front/search-panel/locale/es.yml @@ -1,6 +1,6 @@ Ink: Tinta Origin: Origen -Producer: Productor. +Producer: Productor With visible: Con visible Field: Campo More fields: Más campos diff --git a/modules/order/back/methods/order/getItemTypeAvailable.js b/modules/order/back/methods/order/getItemTypeAvailable.js index 56f6a8c0e..906095f41 100644 --- a/modules/order/back/methods/order/getItemTypeAvailable.js +++ b/modules/order/back/methods/order/getItemTypeAvailable.js @@ -28,30 +28,57 @@ module.exports = Self => { }); Self.getItemTypeAvailable = async(orderId, itemCategoryId) => { - let stmts = []; + const stmts = []; let stmt; - let order = await app.models.Order.findById(orderId); + const order = await app.models.Order.findById(orderId); stmt = new ParameterizedSQL('call vn.available_calc(?, ?, ?)', [ order.landed, order.addressFk, order.agencyModeFk ]); stmts.push(stmt); - stmt = new ParameterizedSQL(` - SELECT it.id, it.name, ic.name categoryName + + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.item'); + + stmt = new ParameterizedSQL( + `CREATE TEMPORARY TABLE tmp.item + (PRIMARY KEY (itemFk)) ENGINE = MEMORY + SELECT DISTINCT + i.id AS itemFk, + it.id AS typeFk, + it.name, + ic.name AS categoryName FROM tmp.availableCalc ac JOIN cache.available a ON a.calc_id = ac.calcFk - JOIN item i ON i.id = a.item_id - JOIN itemType it ON it.id = i.typeFk - JOIN itemCategory ic ON ic.id = it.categoryFk - WHERE it.categoryFk = ? - GROUP BY it.id`, [itemCategoryId] + JOIN vn.item i ON i.id = a.item_id + JOIN vn.itemType it ON it.id = i.typeFk + JOIN vn.itemCategory ic ON ic.id = it.categoryFk + WHERE it.categoryFk = ?`, [itemCategoryId] ); - let categoriesIndex = stmts.push(stmt) - 1; + stmts.push(stmt); - let sql = ParameterizedSQL.join(stmts, ';'); - let result = await Self.rawStmt(sql); + stmt = new ParameterizedSQL( + 'CALL vn.catalog_calculate(?, ?, ?)', [ + order.landed, + order.addressFk, + order.agencyModeFk, + ] + ); + stmts.push(stmt); + + stmt = new ParameterizedSQL(` + SELECT i.typeFk AS id, i.name, i.categoryName + FROM tmp.item i + JOIN tmp.ticketCalculateItem tci ON tci.itemFk = i.itemFk + GROUP BY i.typeFk` + ); + const categoriesIndex = stmts.push(stmt) - 1; + + stmts.push('DROP TEMPORARY TABLE tmp.item'); + + const sql = ParameterizedSQL.join(stmts, ';'); + const result = await Self.rawStmt(sql); return result[categoriesIndex]; }; diff --git a/modules/ticket/back/methods/sale/deleteSales.js b/modules/ticket/back/methods/sale/deleteSales.js index 31899a501..a604da858 100644 --- a/modules/ticket/back/methods/sale/deleteSales.js +++ b/modules/ticket/back/methods/sale/deleteSales.js @@ -6,18 +6,18 @@ module.exports = Self => { accessType: 'WRITE', accepts: [{ arg: 'sales', - type: ['Object'], + type: ['object'], required: true, description: 'The sales to remove' }, { arg: 'ticketId', - type: 'Number', + type: 'number', required: true, description: 'The ticket id' }], returns: { - type: ['Object'], + type: ['object'], root: true }, http: { @@ -27,10 +27,25 @@ module.exports = Self => { }); Self.deleteSales = async(ctx, sales, ticketId) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; const canEditSales = await models.Sale.canEdit(ctx, sales); + const ticket = await models.Ticket.findById(ticketId, { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + }); + const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId); if (!isTicketEditable) throw new UserError(`The sales of this ticket can't be modified`); @@ -39,11 +54,26 @@ module.exports = Self => { throw new UserError(`Sale(s) blocked, please contact production`); const promises = []; + let deletions = ''; for (let sale of sales) { const deletedSale = models.Sale.destroyById(sale.id); + deletions += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`; + promises.push(deletedSale); } + const salesPerson = ticket.client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + + const message = $t('Deleted sales from ticket', { + ticketId: ticketId, + ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`, + deletions: deletions + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + return Promise.all(promises); }; }; diff --git a/modules/ticket/back/methods/sale/reserve.js b/modules/ticket/back/methods/sale/reserve.js index 96c794e7c..639827fa7 100644 --- a/modules/ticket/back/methods/sale/reserve.js +++ b/modules/ticket/back/methods/sale/reserve.js @@ -5,24 +5,27 @@ module.exports = Self => { Self.remoteMethodCtx('reserve', { description: 'Change the state of a ticket', accessType: 'WRITE', - accepts: [{ - arg: 'ticketId', - type: 'Number', - required: true, - description: 'The ticket id' - }, { - arg: 'sales', - type: ['Object'], - required: true, - description: 'The sale to reserve' - }, - { - arg: 'reserved', - type: 'Boolean', - required: true - }], + accepts: [ + { + arg: 'ticketId', + type: 'number', + required: true, + description: 'The ticket id' + }, + { + arg: 'sales', + type: ['object'], + required: true, + description: 'The sale to reserve' + }, + { + arg: 'reserved', + type: 'boolean', + required: true + } + ], returns: { - type: ['Object'], + type: ['object'], root: true }, http: { @@ -32,7 +35,9 @@ module.exports = Self => { }); Self.reserve = async(ctx, ticketId, sales, reserved) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; + const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId); if (!isTicketEditable) throw new UserError(`The sales of this ticket can't be modified`); @@ -42,12 +47,50 @@ module.exports = Self => { if (!canEditSale) throw new UserError(`Sale(s) blocked, please contact production`); + let changesMade = ''; const promises = []; + for (let sale of sales) { - const reservedSale = models.Sale.update({id: sale.id}, {reserved: reserved}); - promises.push(reservedSale); + if (sale.reserved != reserved) { + const oldState = sale.reserved ? 'reserved' : 'regular'; + const newState = reserved ? 'reserved' : 'regular'; + + const reservedSale = models.Sale.update({id: sale.id}, {reserved: reserved}); + + promises.push(reservedSale); + + changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${$t('State')}: ${$t(oldState)} ➔ *${$t(newState)}*`; + } } - return Promise.all(promises); + const result = await Promise.all(promises); + + const ticket = await models.Ticket.findById(ticketId, { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + }); + + const salesPerson = ticket.client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + + const message = $t('Changed sale reserved state', { + ticketId: ticketId, + ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`, + changes: changesMade + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + + return result; }; }; diff --git a/modules/ticket/back/methods/sale/specs/deleteSales.spec.js b/modules/ticket/back/methods/sale/specs/deleteSales.spec.js index 22ac49452..aabc38375 100644 --- a/modules/ticket/back/methods/sale/specs/deleteSales.spec.js +++ b/modules/ticket/back/methods/sale/specs/deleteSales.spec.js @@ -17,7 +17,14 @@ describe('sale deleteSales()', () => { }); it('should throw an error if the ticket of the given sales is not editable', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + let ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; + let error; const sales = [{id: 1, instance: 0}, {id: 2, instance: 1}]; @@ -33,7 +40,13 @@ describe('sale deleteSales()', () => { }); it('should delete the sale', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + let ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const sales = [{id: newSale.id, instance: 0}]; const ticketId = 16; diff --git a/modules/ticket/back/methods/sale/specs/reserve.spec.js b/modules/ticket/back/methods/sale/specs/reserve.spec.js index fce7bd95e..3752a2b6b 100644 --- a/modules/ticket/back/methods/sale/specs/reserve.spec.js +++ b/modules/ticket/back/methods/sale/specs/reserve.spec.js @@ -1,7 +1,14 @@ const app = require('vn-loopback/server/server'); describe('sale reserve()', () => { - const ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; + afterAll(async done => { let ctx = {req: {accessToken: {userId: 9}}}; let params = { diff --git a/modules/ticket/back/methods/sale/specs/updatePrice.spec.js b/modules/ticket/back/methods/sale/specs/updatePrice.spec.js index ec4376adb..ea8a65c27 100644 --- a/modules/ticket/back/methods/sale/specs/updatePrice.spec.js +++ b/modules/ticket/back/methods/sale/specs/updatePrice.spec.js @@ -17,7 +17,13 @@ describe('sale updatePrice()', () => { }); it('should throw an error if the ticket is not editable', async() => { - let ctx = {req: {accessToken: {userId: 18}}}; + const ctx = { + req: { + accessToken: {userId: 18}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; let immutableSaleId = 1; let price = 5; @@ -31,7 +37,14 @@ describe('sale updatePrice()', () => { }); it('should return 0 if the price is an empty string', async() => { - let ctx = {req: {accessToken: {userId: 18}}}; + const ctx = { + req: { + accessToken: {userId: 18}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; + let price = ''; await app.models.Sale.updatePrice(ctx, saleId, price); @@ -46,7 +59,14 @@ describe('sale updatePrice()', () => { }); it('should now set price as a number in a string', async() => { - let ctx = {req: {accessToken: {userId: 18}}}; + const ctx = { + req: { + accessToken: {userId: 18}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; + let price = '8'; await app.models.Sale.updatePrice(ctx, saleId, price); diff --git a/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js b/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js index 16221b55c..dabdac384 100644 --- a/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js +++ b/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js @@ -1,7 +1,13 @@ const app = require('vn-loopback/server/server'); describe('sale updateQuantity()', () => { - const ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; it('should throw an error if the quantity is not a number', async() => { let error; diff --git a/modules/ticket/back/methods/sale/updatePrice.js b/modules/ticket/back/methods/sale/updatePrice.js index 2195c2b7b..cb86296c9 100644 --- a/modules/ticket/back/methods/sale/updatePrice.js +++ b/modules/ticket/back/methods/sale/updatePrice.js @@ -19,7 +19,7 @@ module.exports = Self => { } ], returns: { - type: 'Number', + type: 'number', root: true }, http: { @@ -29,29 +29,37 @@ module.exports = Self => { }); Self.updatePrice = async(ctx, id, newPrice) => { - let models = Self.app.models; - let tx = await Self.beginTransaction({}); + const $t = ctx.req.__; // $translate + const models = Self.app.models; + const tx = await Self.beginTransaction({}); try { - let options = {transaction: tx}; + const options = {transaction: tx}; - let filter = { + const filter = { include: { relation: 'ticket', scope: { include: { relation: 'client', scope: { - fields: ['salesPersonFk'] + fields: ['salesPersonFk'], + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } } }, fields: ['id', 'clientFk'] } } }; - let sale = await models.Sale.findById(id, filter, options); - let isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk); + const sale = await models.Sale.findById(id, filter, options); + + const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk); if (!isEditable) throw new UserError(`The sales of this ticket can't be modified`); @@ -60,21 +68,19 @@ module.exports = Self => { if (!canEditSale) throw new UserError(`Sale(s) blocked, please contact production`); + const oldPrice = sale.price; const userId = ctx.req.accessToken.userId; + const usesMana = await models.WorkerMana.findOne({where: {workerFk: userId}, fields: 'amount'}, options); + const componentCode = usesMana ? 'mana' : 'buyerDiscount'; + const discount = await models.Component.findOne({where: {code: componentCode}}, options); + const componentId = discount.id; + const componentValue = newPrice - sale.price; - let usesMana = await models.WorkerMana.findOne({where: {workerFk: userId}, fields: 'amount'}, options); - - let componentCode = usesMana ? 'mana' : 'buyerDiscount'; - - let discount = await models.Component.findOne({where: {code: componentCode}}, options); - let componentId = discount.id; - let componentValue = newPrice - sale.price; - - let where = { + const where = { componentFk: componentId, saleFk: id }; - let saleComponent = await models.SaleComponent.findOne({where}, options); + const saleComponent = await models.SaleComponent.findOne({where}, options); if (saleComponent) { await models.SaleComponent.updateAll(where, { @@ -92,6 +98,22 @@ module.exports = Self => { query = `CALL vn.manaSpellersRequery(?)`; await Self.rawSql(query, [userId], options); + const salesPerson = sale.ticket().client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + const message = $t('Changed sale price', { + ticketId: sale.ticket().id, + itemId: sale.itemFk, + concept: sale.concept, + quantity: sale.quantity, + oldPrice: oldPrice, + newPrice: newPrice, + ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`, + itemUrl: `${origin}/#!/item/${sale.itemFk}/summary` + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + await tx.commit(); return sale; diff --git a/modules/ticket/back/methods/sale/updateQuantity.js b/modules/ticket/back/methods/sale/updateQuantity.js index 00df49b9f..62e09f1f5 100644 --- a/modules/ticket/back/methods/sale/updateQuantity.js +++ b/modules/ticket/back/methods/sale/updateQuantity.js @@ -26,7 +26,8 @@ module.exports = Self => { } }); - Self.updateQuantity = async(ctx, id, quantity) => { + Self.updateQuantity = async(ctx, id, newQuantity) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; const canEditSale = await models.Sale.canEdit(ctx, [id]); @@ -34,13 +35,51 @@ module.exports = Self => { if (!canEditSale) throw new UserError(`Sale(s) blocked, please contact production`); - if (isNaN(quantity)) + if (isNaN(newQuantity)) throw new UserError(`The value should be a number`); - let currentLine = await models.Sale.findOne({where: {id: id}}); - if (quantity > currentLine.quantity) + const filter = { + include: { + relation: 'ticket', + scope: { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + } + } + }; + + const sale = await models.Sale.findById(id, filter); + + if (newQuantity > sale.quantity) throw new UserError('The new quantity should be smaller than the old one'); - return await currentLine.updateAttributes({quantity: quantity}); + const oldQuantity = sale.quantity; + const result = await sale.updateAttributes({quantity: newQuantity}); + + const salesPerson = sale.ticket().client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + const message = $t('Changed sale quantity', { + ticketId: sale.ticket().id, + itemId: sale.itemFk, + concept: sale.concept, + oldQuantity: oldQuantity, + newQuantity: newQuantity, + ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`, + itemUrl: `${origin}/#!/item/${sale.itemFk}/summary` + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + + return result; }; }; diff --git a/modules/ticket/back/methods/ticket/addSale.js b/modules/ticket/back/methods/ticket/addSale.js index 365355df2..dc45e5de9 100644 --- a/modules/ticket/back/methods/ticket/addSale.js +++ b/modules/ticket/back/methods/ticket/addSale.js @@ -33,6 +33,7 @@ module.exports = Self => { }); Self.addSale = async(ctx, id, itemId, quantity) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; const isEditable = await models.Ticket.isEditable(ctx, id); @@ -40,7 +41,19 @@ module.exports = Self => { throw new UserError(`The sales of this ticket can't be modified`); const item = await models.Item.findById(itemId); - const ticket = await models.Ticket.findById(id); + const ticket = await models.Ticket.findById(id, { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + }); const res = await models.Item.getVisibleAvailable(itemId, ticket.warehouseFk, ticket.shipped); @@ -63,6 +76,20 @@ module.exports = Self => { } }); + const addition = `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`; + + const salesPerson = ticket.client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + + const message = $t('Added sale to ticket', { + ticketId: id, + ticketUrl: `${origin}/#!/ticket/${id}/sale`, + addition: addition + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + return sale; }; }; diff --git a/modules/ticket/back/methods/ticket/componentUpdate.js b/modules/ticket/back/methods/ticket/componentUpdate.js index b66179eb8..9ebd51bf4 100644 --- a/modules/ticket/back/methods/ticket/componentUpdate.js +++ b/modules/ticket/back/methods/ticket/componentUpdate.js @@ -230,7 +230,7 @@ module.exports = Self => { ticketUrl: `${origin}/#!/ticket/${args.id}/sale`, changes: changesMade }); - await models.Chat.sendCheckingPresence(ctx, salesPersonId, message, myOptions); + await models.Chat.sendCheckingPresence(ctx, salesPersonId, message); } if (tx) await tx.commit(); diff --git a/modules/ticket/back/methods/ticket/specs/addSale.spec.js b/modules/ticket/back/methods/ticket/specs/addSale.spec.js index c2650bf4b..9f6da7ed2 100644 --- a/modules/ticket/back/methods/ticket/specs/addSale.spec.js +++ b/modules/ticket/back/methods/ticket/specs/addSale.spec.js @@ -12,7 +12,13 @@ describe('ticket addSale()', () => { }); it('should create a new sale for the ticket with id 13', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const itemId = 4; const quantity = 10; newSale = await app.models.Ticket.addSale(ctx, ticketId, itemId, quantity); @@ -21,7 +27,13 @@ describe('ticket addSale()', () => { }); it('should not be able to add a sale if the item quantity is not available', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const itemId = 11; const quantity = 10; @@ -36,7 +48,13 @@ describe('ticket addSale()', () => { }); it('should not be able to add a sale if the ticket is not editable', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const notEditableTicketId = 1; const itemId = 4; const quantity = 10; diff --git a/modules/ticket/back/methods/ticket/specs/updateDiscount.spec.js b/modules/ticket/back/methods/ticket/specs/updateDiscount.spec.js index 2e71842b4..406a3f3ee 100644 --- a/modules/ticket/back/methods/ticket/specs/updateDiscount.spec.js +++ b/modules/ticket/back/methods/ticket/specs/updateDiscount.spec.js @@ -35,7 +35,13 @@ describe('sale updateDiscount()', () => { }); it('should throw an error if no sales were selected', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; let error; const ticketId = 11; const sales = []; @@ -51,7 +57,13 @@ describe('sale updateDiscount()', () => { }); it('should throw an error if no sales belong to different tickets', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; let error; const ticketId = 11; const sales = [1, 14]; @@ -67,7 +79,13 @@ describe('sale updateDiscount()', () => { }); it('should throw an error if the ticket is invoiced already', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; let error; const ticketId = 1; const sales = [1]; @@ -83,7 +101,13 @@ describe('sale updateDiscount()', () => { }); it('should update the discount if the salesPerson has mana', async() => { - let ctx = {req: {accessToken: {userId: 18}}}; + const ctx = { + req: { + accessToken: {userId: 18}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const ticketId = 11; const sales = [originalSaleId]; const newDiscount = 100; @@ -105,7 +129,13 @@ describe('sale updateDiscount()', () => { }); it('should update the discount and add company discount component if the worker does not have mana', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const ticketId = 11; const sales = [originalSaleId]; const newDiscount = 100; diff --git a/modules/ticket/back/methods/ticket/updateDiscount.js b/modules/ticket/back/methods/ticket/updateDiscount.js index 1bebaae8d..c9424395d 100644 --- a/modules/ticket/back/methods/ticket/updateDiscount.js +++ b/modules/ticket/back/methods/ticket/updateDiscount.js @@ -17,7 +17,8 @@ module.exports = Self => { description: 'The sales id', type: ['number'], required: true, - }, { + }, + { arg: 'newDiscount', description: 'The new discount', type: 'number', @@ -35,6 +36,7 @@ module.exports = Self => { }); Self.updateDiscount = async(ctx, id, salesIds, newDiscount) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; const tx = await Self.beginTransaction({}); @@ -59,7 +61,7 @@ module.exports = Self => { } }; - let sales = await models.Sale.find(filter, options); + const sales = await models.Sale.find(filter, options); if (sales.length === 0) throw new UserError('Please select at least one sale'); @@ -94,8 +96,11 @@ module.exports = Self => { where: {code: componentCode}}, options); const componentId = discountComponent.id; - let promises = []; + const promises = []; + let changesMade = ''; + for (let sale of sales) { + const oldDiscount = sale.discount; const value = ((-sale.price * newDiscount) / 100); const newComponent = models.SaleComponent.upsert({ saleFk: sale.id, @@ -105,6 +110,7 @@ module.exports = Self => { const updatedSale = sale.updateAttribute('discount', newDiscount, options); promises.push(newComponent, updatedSale); + changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${oldDiscount}% ➔ *${newDiscount}%*`; } await Promise.all(promises); @@ -112,6 +118,32 @@ module.exports = Self => { const query = `call vn.manaSpellersRequery(?)`; await Self.rawSql(query, [userId], options); + const ticket = await models.Ticket.findById(id, { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + }, options); + + const salesPerson = ticket.client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + + const message = $t('Changed sale discount', { + ticketId: id, + ticketUrl: `${origin}/#!/ticket/${id}/sale`, + changes: changesMade + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + await tx.commit(); } catch (error) { await tx.rollback(); diff --git a/modules/ticket/front/component/index.js b/modules/ticket/front/component/index.js index 307f8af15..3f262f457 100644 --- a/modules/ticket/front/component/index.js +++ b/modules/ticket/front/component/index.js @@ -82,7 +82,10 @@ class Controller extends Section { if (!this.ticket) return; this.$http.get(`Tickets/${this.ticket.id}/getVolume`) - .then(res => this.ticketVolume = res.data[0].volume); + .then(res => { + if (res.data.length) + this.ticketVolume = res.data[0].volume; + }); } } diff --git a/modules/ticket/front/sale/index.js b/modules/ticket/front/sale/index.js index 46cbe43c9..7e7654038 100644 --- a/modules/ticket/front/sale/index.js +++ b/modules/ticket/front/sale/index.js @@ -47,7 +47,13 @@ class Controller extends Section { getMana() { this.$http.get(`Tickets/${this.$params.id}/getSalesPersonMana`) - .then(res => this.edit.mana = res.data); + .then(res => { + this.edit.mana = res.data; + this.$.$applyAsync(() => { + this.$.editDiscount.relocate(); + this.$.editPricePopover.relocate(); + }); + }); } /** diff --git a/modules/ticket/front/sale/index.spec.js b/modules/ticket/front/sale/index.spec.js index e5368468a..169b41c5f 100644 --- a/modules/ticket/front/sale/index.spec.js +++ b/modules/ticket/front/sale/index.spec.js @@ -42,6 +42,8 @@ describe('Ticket', () => { $scope.sms = {open: () => {}}; $scope.ticket = ticket; $scope.model = crudModel; + $scope.editDiscount = {relocate: () => {}}; + $scope.editPricePopover = {relocate: () => {}}; $httpBackend = _$httpBackend_; Object.defineProperties($state.params, { id: {