diff --git a/src/composables/useAdvancedSummary.js b/src/composables/useAdvancedSummary.js
new file mode 100644
index 000000000..98b998d2a
--- /dev/null
+++ b/src/composables/useAdvancedSummary.js
@@ -0,0 +1,11 @@
+import axios from 'axios';
+import { useRole } from './useRole';
+
+export async function useAdvancedSummary(model, id, roles = ['hr']) {
+ if (useRole().hasAny(roles)) {
+ const { data } = await axios.get(`${model}/advancedSummary`, {
+ params: { filter: { where: { id } } },
+ });
+ return Array.isArray(data) ? data[0] : data;
+ }
+}
diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index c1748c8ff..10186d92a 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -278,6 +278,7 @@ globals:
RouteExtendedList: Router
wasteRecalc: Waste recaclulate
operator: Operator
+ parking: Parking
supplier: Supplier
created: Created
worker: Worker
@@ -312,6 +313,7 @@ globals:
changePass: Change password
deleteConfirmTitle: Delete selected elements
changeState: Change state
+ raid: 'Raid {daysInForward} days'
errors:
statusUnauthorized: Access denied
statusInternalServerError: An internal server error has ocurred
@@ -369,7 +371,6 @@ entry:
companyFk: Company
travelFk: Travel
isExcludedFromAvailable: Inventory
- isRaid: Raid
invoiceAmount: Import
summary:
commission: Commission
@@ -380,7 +381,6 @@ entry:
ordered: Ordered
confirmed: Confirmed
booked: Booked
- raid: Raid
excludedFromAvailable: Inventory
travelReference: Reference
travelAgency: Agency
@@ -413,7 +413,6 @@ entry:
ordered: Ordered
confirmed: Confirmed
booked: Booked
- raid: Raid
excludedFromAvailable: Inventory
agency: Agency
warehouseOut: Warehouse Out
@@ -476,7 +475,6 @@ entry:
packingOut: Package out
landing: Landing
isExcludedFromAvailable: Es inventory
- isRaid: Raid
ticket:
pageTitles:
tickets: Tickets
@@ -663,8 +661,6 @@ parking:
sector: Sector
row: Row
column: Column
- pageTitles:
- parking: Parking
searchBar:
info: You can search by parking code
label: Search parking...
@@ -1042,6 +1038,7 @@ travel:
warehouseIn: Warehouse In
delivered: Delivered
received: Received
+ daysInForward: Days in forward
thermographs:
code: Code
temperature: Temperature
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index bd414a793..66b5efd52 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -282,6 +282,7 @@ globals:
medical: Mutua
wasteRecalc: Recalcular mermas
operator: Operario
+ parking: Parking
supplier: Proveedor
created: Fecha creación
worker: Trabajador
@@ -316,6 +317,7 @@ globals:
changePass: Cambiar contraseña
deleteConfirmTitle: Eliminar los elementos seleccionados
changeState: Cambiar estado
+ raid: 'Redada {daysInForward} días'
errors:
statusUnauthorized: Acceso denegado
statusInternalServerError: Ha ocurrido un error interno del servidor
@@ -371,7 +373,6 @@ entry:
companyFk: Empresa
travelFk: Envio
isExcludedFromAvailable: Inventario
- isRaid: Redada
invoiceAmount: Importe
summary:
commission: Comisión
@@ -382,7 +383,6 @@ entry:
ordered: Pedida
confirmed: Confirmada
booked: Contabilizada
- raid: Redada
excludedFromAvailable: Inventario
travelReference: Referencia
travelAgency: Agencia
@@ -415,7 +415,6 @@ entry:
ordered: Pedida
confirmed: Confirmado
booked: Asentado
- raid: Redada
excludedFromAvailable: Inventario
agency: Agencia
warehouseOut: Alm. salida
@@ -478,7 +477,6 @@ entry:
packingOut: Embalaje envíos
landing: Llegada
isExcludedFromAvailable: Es inventario
- isRaid: Redada
ticket:
pageTitles:
tickets: Tickets
@@ -710,8 +708,6 @@ parking:
pickingOrder: Orden de recogida
row: Fila
column: Columna
- pageTitles:
- parking: Parking
searchBar:
info: Puedes buscar por código de parking
label: Buscar parking...
@@ -1040,6 +1036,7 @@ travel:
warehouseIn: Alm. entrada
delivered: Enviada
received: Recibida
+ daysInForward: Días redada
thermographs:
code: Código
temperature: Temperatura
diff --git a/src/pages/Entry/Card/EntryBasicData.vue b/src/pages/Entry/Card/EntryBasicData.vue
index b81b1db22..3288616fb 100644
--- a/src/pages/Entry/Card/EntryBasicData.vue
+++ b/src/pages/Entry/Card/EntryBasicData.vue
@@ -168,7 +168,6 @@ const onFilterTravelSelected = (formData, id) => {
v-model="data.isExcludedFromAvailable"
:label="t('entry.basicData.excludedFromAvailable')"
/>
-
(data.value = useCardDescription(entity.supplier?.nickname, entity.id));
-const currentEntry = computed(() => state.get('entry'));
-
const getEntryRedirectionFilter = (entry) => {
let entryTravel = entry && entry.travel;
@@ -133,10 +136,10 @@ watch;
:value="entity.travel?.warehouseOut?.name"
/>
-
+
{{ t('Inventory entry') }}
- {{ t('Virtual entry') }}
+
+ {{
+ t('globals.raid', {
+ daysInForward: entity?.travel?.daysInForward,
+ })
+ }}
diff --git a/src/pages/Entry/Card/EntrySummary.vue b/src/pages/Entry/Card/EntrySummary.vue
index c2f1e6b57..62e13551a 100644
--- a/src/pages/Entry/Card/EntrySummary.vue
+++ b/src/pages/Entry/Card/EntrySummary.vue
@@ -259,11 +259,6 @@ const fetchEntryBuys = async () => {
v-model="entry.isBooked"
:disable="true"
/>
-
[
:loading="isLoading"
@click="
openReport(
- `Entries/${props.row.id}/buy-label`
+ `Entries/${props.row.id}/buy-label-supplier`
)
"
unelevated
diff --git a/src/pages/Entry/EntryList.vue b/src/pages/Entry/EntryList.vue
index 6f7ff1935..e9179c239 100644
--- a/src/pages/Entry/EntryList.vue
+++ b/src/pages/Entry/EntryList.vue
@@ -41,6 +41,10 @@ const entryFilter = {
};
const columns = computed(() => [
+ {
+ name: 'status',
+ columnFilter: false,
+ },
{
align: 'left',
label: t('entry.list.tableVisibleColumns.id'),
@@ -154,27 +158,8 @@ const columns = computed(() => [
cardVisible: true,
},
{
- align: 'left',
label: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'),
name: 'isExcludedFromAvailable',
- chip: {
- color: null,
- condition: (value) => value,
- icon: 'vn:inventory',
- },
- columnFilter: {
- inWhere: true,
- },
- },
- {
- align: 'left',
- label: t('entry.list.tableVisibleColumns.isRaid'),
- name: 'isRaid',
- chip: {
- color: null,
- condition: (value) => value,
- icon: 'vn:net',
- },
columnFilter: {
inWhere: true,
},
@@ -225,6 +210,26 @@ onMounted(async () => {
auto-load
:right-search="false"
>
+
+
+
+ {{
+ t('entry.list.tableVisibleColumns.isExcludedFromAvailable')
+ }}
+
+
+
+ {{
+ t('globals.raid', { daysInForward: row.daysInForward })
+ }}
+
+
+
{{ row.supplierName }}
diff --git a/src/pages/Entry/locale/en.yml b/src/pages/Entry/locale/en.yml
index f9dbd0589..cd5113d84 100644
--- a/src/pages/Entry/locale/en.yml
+++ b/src/pages/Entry/locale/en.yml
@@ -1,7 +1,6 @@
entryList:
list:
inventoryEntry: Inventory entry
- virtualEntry: Virtual entry
entryFilter:
filter:
search: General search
diff --git a/src/pages/Entry/locale/es.yml b/src/pages/Entry/locale/es.yml
index feeea1fc9..3007c5d44 100644
--- a/src/pages/Entry/locale/es.yml
+++ b/src/pages/Entry/locale/es.yml
@@ -4,7 +4,6 @@ You can search by entry reference: Puedes buscar por referencia de la entrada
entryList:
list:
inventoryEntry: Es inventario
- virtualEntry: Es una redada
entryFilter:
filter:
search: Búsqueda general
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index cca5560fe..a6873d10c 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -40,6 +40,12 @@ const itemFilter = {
fields: ['id', 'name'],
},
},
+ {
+ relation: 'production',
+ scope: {
+ fields: ['id', 'name'],
+ },
+ },
],
};
const columns = computed(() => [
@@ -161,19 +167,13 @@ const columns = computed(() => [
name: 'intrastat',
align: 'left',
component: 'select',
- attrs: {
- url: 'Intrastats',
- optionValue: 'description',
- optionLabel: 'description',
- },
columnFilter: {
- name: 'description',
+ name: 'intrastat',
attrs: {
url: 'Intrastats',
optionValue: 'description',
optionLabel: 'description',
},
- alias: 'intr',
},
columnField: {
component: null,
@@ -211,14 +211,19 @@ const columns = computed(() => [
label: t('item.list.userName'),
name: 'userName',
align: 'left',
+ component: 'select',
columnFilter: {
name: 'workerFk',
attrs: {
- url: 'Users',
+ url: 'VnUsers/preview',
optionValue: 'id',
- optionLabel: 'userName',
+ optionLabel: 'nickname',
},
},
+
+ columnField: {
+ component: null,
+ },
},
{
label: t('item.list.weightByPiece'),
@@ -255,9 +260,13 @@ const columns = computed(() => [
name: 'producer',
align: 'left',
component: 'select',
- attrs: {
- url: 'Producers',
- fields: ['id', 'name'],
+ columnFilter: {
+ name: 'producerFk',
+ attrs: {
+ url: 'Producers',
+ optionValue: 'id',
+ optionLabel: 'name',
+ },
},
columnField: {
component: null,
@@ -331,7 +340,7 @@ const columns = computed(() => [
{{ row.userName }}
-
+
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 450031a0e..36da0368b 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -70,6 +70,18 @@ const columns = computed(() => [
{
label: t('item.buyRequest.requester'),
name: 'requesterName',
+ component: 'select',
+ columnFilter: {
+ name: 'requesterFk',
+ attrs: {
+ url: 'VnUsers/preview',
+ optionValue: 'id',
+ optionLabel: 'nickname',
+ },
+ },
+ columnField: {
+ component: null,
+ },
columnClass: 'shrink',
},
{
@@ -88,6 +100,18 @@ const columns = computed(() => [
label: t('item.buyRequest.attender'),
name: 'attenderName',
align: 'left',
+ component: 'select',
+ columnFilter: {
+ name: 'attenderFk',
+ attrs: {
+ url: 'VnUsers/preview',
+ optionValue: 'id',
+ optionLabel: 'nickname',
+ },
+ },
+ columnField: {
+ component: null,
+ },
columnClass: 'shrink',
},
{
diff --git a/src/pages/ItemType/Card/ItemTypeBasicData.vue b/src/pages/Item/ItemType/Card/ItemTypeBasicData.vue
similarity index 100%
rename from src/pages/ItemType/Card/ItemTypeBasicData.vue
rename to src/pages/Item/ItemType/Card/ItemTypeBasicData.vue
diff --git a/src/pages/ItemType/Card/ItemTypeCard.vue b/src/pages/Item/ItemType/Card/ItemTypeCard.vue
similarity index 75%
rename from src/pages/ItemType/Card/ItemTypeCard.vue
rename to src/pages/Item/ItemType/Card/ItemTypeCard.vue
index 9daec7921..0a2706a07 100644
--- a/src/pages/ItemType/Card/ItemTypeCard.vue
+++ b/src/pages/Item/ItemType/Card/ItemTypeCard.vue
@@ -1,7 +1,7 @@
diff --git a/src/pages/ItemType/Card/ItemTypeDescriptor.vue b/src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue
similarity index 100%
rename from src/pages/ItemType/Card/ItemTypeDescriptor.vue
rename to src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue
diff --git a/src/pages/ItemType/Card/ItemTypeLog.vue b/src/pages/Item/ItemType/Card/ItemTypeLog.vue
similarity index 100%
rename from src/pages/ItemType/Card/ItemTypeLog.vue
rename to src/pages/Item/ItemType/Card/ItemTypeLog.vue
diff --git a/src/pages/ItemType/Card/ItemTypeSummary.vue b/src/pages/Item/ItemType/Card/ItemTypeSummary.vue
similarity index 100%
rename from src/pages/ItemType/Card/ItemTypeSummary.vue
rename to src/pages/Item/ItemType/Card/ItemTypeSummary.vue
diff --git a/src/pages/ItemType/ItemTypeFilter.vue b/src/pages/Item/ItemType/ItemTypeFilter.vue
similarity index 57%
rename from src/pages/ItemType/ItemTypeFilter.vue
rename to src/pages/Item/ItemType/ItemTypeFilter.vue
index 2a86795c2..f6fb73098 100644
--- a/src/pages/ItemType/ItemTypeFilter.vue
+++ b/src/pages/Item/ItemType/ItemTypeFilter.vue
@@ -12,6 +12,39 @@ const props = defineProps({
});
const emit = defineEmits(['search']);
+const exprBuilder = (param, value) => {
+ switch (param) {
+ case 'name':
+ return {
+ name: { like: `%${value}%` },
+ };
+ case 'code':
+ return {
+ code: { like: `%${value}%` },
+ };
+ case 'search':
+ if (value) {
+ if (!isNaN(value)) {
+ return { id: value };
+ } else {
+ return {
+ or: [
+ {
+ name: {
+ like: `%${value}%`,
+ },
+ },
+ {
+ code: {
+ like: `%${value}%`,
+ },
+ },
+ ],
+ };
+ }
+ }
+ }
+};
@@ -19,6 +52,8 @@ const emit = defineEmits(['search']);
:data-key="props.dataKey"
:search-button="true"
@search="emit('search')"
+ search-url="table"
+ :expr-builder="exprBuilder"
>
diff --git a/src/pages/ItemType/ItemTypeSearchbar.vue b/src/pages/Item/ItemType/ItemTypeSearchbar.vue
similarity index 94%
rename from src/pages/ItemType/ItemTypeSearchbar.vue
rename to src/pages/Item/ItemType/ItemTypeSearchbar.vue
index 749033d43..87903a517 100644
--- a/src/pages/ItemType/ItemTypeSearchbar.vue
+++ b/src/pages/Item/ItemType/ItemTypeSearchbar.vue
@@ -10,6 +10,7 @@ const { t } = useI18n();
url="ItemTypes"
:label="t('Search item type')"
:info="t('Search itemType by id, name or code')"
+ search-url="table"
/>
diff --git a/src/pages/ItemType/locale/en.yml b/src/pages/Item/ItemType/locale/en.yml
similarity index 100%
rename from src/pages/ItemType/locale/en.yml
rename to src/pages/Item/ItemType/locale/en.yml
diff --git a/src/pages/ItemType/locale/es.yml b/src/pages/Item/ItemType/locale/es.yml
similarity index 100%
rename from src/pages/ItemType/locale/es.yml
rename to src/pages/Item/ItemType/locale/es.yml
diff --git a/src/pages/Item/ItemTypeCreate.vue b/src/pages/Item/ItemTypeCreate.vue
deleted file mode 100644
index 60c037510..000000000
--- a/src/pages/Item/ItemTypeCreate.vue
+++ /dev/null
@@ -1,98 +0,0 @@
-
-
-
- (categoriesOptions = data)"
- :filter="{ order: 'name ASC', fields: ['id', 'name'] }"
- auto-load
- />
- (temperaturesOptions = data)"
- :filter="{ order: 'name ASC', fields: ['code', 'name'] }"
- auto-load
- />
-
-
-
-
-
-
-
-
-
-
-
-
- {{ scope.opt?.name }}
- {{ scope.opt?.nickname }},
- {{ scope.opt?.code }}
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue
index 13dd56a42..149de482d 100644
--- a/src/pages/Item/ItemTypeList.vue
+++ b/src/pages/Item/ItemTypeList.vue
@@ -1,13 +1,16 @@
diff --git a/src/pages/Parking/ParkingList.vue b/src/pages/Parking/ParkingList.vue
index b6f4e8146..109613383 100644
--- a/src/pages/Parking/ParkingList.vue
+++ b/src/pages/Parking/ParkingList.vue
@@ -22,7 +22,6 @@ onUnmounted(() => (stateStore.rightDrawer = false));
const filter = {
fields: ['id', 'sectorFk', 'code', 'pickingOrder'],
- include: [{ relation: 'sector', scope: { fields: ['id', 'description'] } }],
};
function exprBuilder(param, value) {
@@ -55,10 +54,9 @@ function exprBuilder(param, value) {
diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue
index 834fced87..bf4a1efb4 100644
--- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue
+++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue
@@ -348,7 +348,6 @@ async function hasDocuware() {
}
async function uploadDocuware(force) {
- console.log('force: ', force);
if (!force)
return quasar
.dialog({
diff --git a/src/pages/Ticket/Card/TicketExpedition.vue b/src/pages/Ticket/Card/TicketExpedition.vue
index 26df4e988..f13613ddd 100644
--- a/src/pages/Ticket/Card/TicketExpedition.vue
+++ b/src/pages/Ticket/Card/TicketExpedition.vue
@@ -224,6 +224,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
route.params.id,
@@ -214,7 +217,9 @@ const addSale = async (sale) => {
}
};
-const changeQuantity = (sale) => {
+const changeQuantity = async (sale) => {
+ canProceed.value = await isSalePrepared(sale);
+ if (!canProceed.value) return;
if (
!sale.itemFk ||
sale.quantity == null ||
@@ -226,6 +231,8 @@ const changeQuantity = (sale) => {
};
const updateConcept = async (sale) => {
+ canProceed.value = await isSalePrepared(sale);
+ if (!canProceed.value) return;
try {
const data = { newConcept: sale.concept };
await axios.post(`Sales/${sale.id}/updateConcept`, data);
@@ -286,6 +293,8 @@ const onOpenEditDiscountPopover = async (sale) => {
};
const updatePrice = async (sale) => {
+ canProceed.value = await isSalePrepared(sale);
+ if (!canProceed.value) return;
try {
const newPrice = edit.value.price;
if (newPrice != null && newPrice != sale.price) {
@@ -300,12 +309,18 @@ const updatePrice = async (sale) => {
}
};
-const changeDiscount = (sale) => {
+const changeDiscount = async (sale) => {
+ canProceed.value = await isSalePrepared(sale);
+ if (!canProceed.value) return;
const newDiscount = edit.value.discount;
if (newDiscount != null && newDiscount != sale.discount) updateDiscount([sale]);
};
const updateDiscount = async (sales, newDiscount = null) => {
+ for (const sale of sales) {
+ const canProceed = await isSalePrepared(sale);
+ if (!canProceed) return;
+ }
const saleIds = sales.map((sale) => sale.id);
const _newDiscount = newDiscount || edit.value.discount;
const params = {
@@ -433,7 +448,9 @@ onUnmounted(() => (stateStore.rightDrawer = false));
const items = ref([]);
const newRow = ref({});
-const updateItem = (row) => {
+const updateItem = async (row) => {
+ canProceed.value = await isSalePrepared(row);
+ if (!canProceed.value) return;
const selectedItem = items.value.find((item) => item.id === row.itemFk);
if (selectedItem) {
row.item = selectedItem;
@@ -476,6 +493,55 @@ const endNewRow = (row) => {
}
};
+async function isSalePrepared(item) {
+ const filter = {
+ params: {
+ where: { ticketFk: route.params.id },
+ order: ['concept ASC', 'quantity DESC'],
+ },
+ };
+ const { data } = await axios.get(`SaleTrackings/${route.params.id}/filter`, {
+ params: {
+ filter: JSON.stringify(filter),
+ },
+ });
+
+ const matchingSale = data.find((sale) => sale.itemFk === item.itemFk);
+ if (!matchingSale) {
+ return true;
+ }
+
+ if (
+ matchingSale.hasSaleGroupDetail ||
+ matchingSale.isControled ||
+ matchingSale.isPrepared ||
+ matchingSale.isPrevious ||
+ matchingSale.isPreviousSelected
+ ) {
+ try {
+ await new Promise((resolve, reject) => {
+ quasar
+ .dialog({
+ component: VnConfirm,
+ componentProps: {
+ title: t('Item prepared'),
+ message: t(
+ 'This item is already prepared. Do you want to continue?'
+ ),
+ data: item,
+ },
+ })
+ .onOk(() => resolve(true))
+ .onCancel(() => reject(new Error('cancelled')));
+ });
+ } catch (error) {
+ tableRef.value.reload();
+ return false;
+ }
+ }
+ return true;
+}
+
watch(
() => newRow.value.itemFk,
(newItemFk) => {
@@ -821,4 +887,6 @@ es:
You are going to delete lines of the ticket: Vas a eliminar lineas del ticket
Add item: Añadir artículo
Transfer lines: Transferir líneas
+ Item prepared: Artículo preparado
+ This item is already prepared. Do you want to continue?: Este artículo ya esta preparado. Desea continuar?
diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue
index 7db2b54b3..bdd980c07 100644
--- a/src/pages/Ticket/TicketAdvance.vue
+++ b/src/pages/Ticket/TicketAdvance.vue
@@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
-import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import VnProgress from 'src/components/common/VnProgressModal.vue';
import RightMenu from 'src/components/common/RightMenu.vue';
import TicketAdvanceFilter from './TicketAdvanceFilter.vue';
diff --git a/src/pages/Travel/Card/TravelBasicData.vue b/src/pages/Travel/Card/TravelBasicData.vue
index a3620a6ba..d6245e655 100644
--- a/src/pages/Travel/Card/TravelBasicData.vue
+++ b/src/pages/Travel/Card/TravelBasicData.vue
@@ -72,6 +72,16 @@ const agenciesOptions = ref([]);
+
+
+
+ {{ t('raidDays') }}
+
+
+
+
+
+es:
+ raidDays: Al rellenarlo, generamos una redada. Indica los días que un travel se moverá automáticamente en el tiempo
+en:
+ raidDays: When filling, a raid is generated. Enter the number of days the travel will automatically forward in time
+
diff --git a/src/pages/Travel/Card/TravelDescriptor.vue b/src/pages/Travel/Card/TravelDescriptor.vue
index bda29903b..6025ad045 100644
--- a/src/pages/Travel/Card/TravelDescriptor.vue
+++ b/src/pages/Travel/Card/TravelDescriptor.vue
@@ -32,6 +32,7 @@ const filter = {
'warehouseOutFk',
'cargoSupplierFk',
'agencyModeFk',
+ 'daysInForward',
],
include: [
{
@@ -77,6 +78,22 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
+
+
+
+
+ {{
+ t('globals.raid', { daysInForward: entity.daysInForward })
+ }}
+
+
+
`#/travel/${entityId.value}/${param}`;
+
diff --git a/src/pages/Travel/TravelList.vue b/src/pages/Travel/TravelList.vue
index a8c0e69cb..334640bff 100644
--- a/src/pages/Travel/TravelList.vue
+++ b/src/pages/Travel/TravelList.vue
@@ -45,6 +45,10 @@ const redirectCreateEntryView = (travelData) => {
};
const columns = computed(() => [
+ {
+ name: 'status',
+ columnFilter: false,
+ },
{
align: 'left',
name: 'id',
@@ -221,6 +225,17 @@ const columns = computed(() => [
:is-editable="false"
:use-model="true"
>
+
+
+
+
+ {{
+ t('globals.raid', { daysInForward: row.daysInForward })
+ }}
+
+
+
-import { ref } from 'vue';
+import { ref, onBeforeMount } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import VnInputDate from 'src/components/common/VnInputDate.vue';
@@ -8,32 +8,22 @@ import FormModel from 'src/components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
+import { useAdvancedSummary } from 'src/composables/useAdvancedSummary';
-const route = useRoute();
const { t } = useI18n();
-
const educationLevels = ref([]);
const countries = ref([]);
const maritalStatus = [
{ code: 'M', name: t('Married') },
{ code: 'S', name: t('Single') },
];
+const advancedSummary = ref({});
-const workerFilter = {
- include: [
- {
- relation: 'user',
- scope: {
- fields: ['name', 'emailVerified'],
- include: { relation: 'emailUser', scope: { fields: ['email'] } },
- },
- },
- { relation: 'sip', scope: { fields: ['extension', 'secret'] } },
- { relation: 'department', scope: { include: { relation: 'department' } } },
- ],
-};
+onBeforeMount(async () => {
+ advancedSummary.value =
+ (await useAdvancedSummary('Workers', +useRoute().params.id)) ?? {};
+});
-
{
+ Object.assign(data, advancedSummary);
+ }
+ "
>
@@ -134,7 +130,7 @@ const workerFilter = {
-
+
useRoute().params.id);
const filter = computed(() => ({
where: {
- gender: store.data?.sex,
+ gender: store.data?.[0]?.sex,
or: [{ workerFk: null }, { workerFk: entityId.value }],
},
}));
@@ -51,6 +51,7 @@ const init = async (data) => {
>
$props.id || route.params.id);
const basicDataUrl = ref(null);
-const isHr = computed(() => useRole().hasAny(['hr']));
const advancedSummary = ref();
onBeforeMount(async () => {
- if (isHr.value) {
- advancedSummary.value = (
- await axios.get('Workers/advancedSummary', {
- params: { filter: { where: { id: entityId.value } } },
- })
- ).data[0];
- basicDataUrl.value = `#/worker/${entityId.value}/basic-data`;
- }
+ advancedSummary.value = await useAdvancedSummary('Workers', entityId.value);
+ basicDataUrl.value = `#/worker/${entityId.value}/basic-data`;
});
@@ -101,21 +93,27 @@ onBeforeMount(async () => {
:label="t('worker.summary.seniority')"
:value="toDate(worker.seniority)"
/>
-
-
+
+
+
-
diff --git a/src/router/modules/item.js b/src/router/modules/item.js
index 2838c3be7..0f810434c 100644
--- a/src/router/modules/item.js
+++ b/src/router/modules/item.js
@@ -97,14 +97,6 @@ export default {
},
component: () => import('src/pages/Item/ItemTypeList.vue'),
},
- {
- path: 'item-type-list/create',
- name: 'ItemTypeCreate',
- meta: {
- title: 'itemTypeCreate',
- },
- component: () => import('src/pages/Item/ItemTypeCreate.vue'),
- },
],
},
{
diff --git a/src/router/modules/itemType.js b/src/router/modules/itemType.js
index 0fd3797e6..1ceecd4cc 100644
--- a/src/router/modules/itemType.js
+++ b/src/router/modules/itemType.js
@@ -18,7 +18,7 @@ export default {
{
name: 'ItemTypeCard',
path: ':id',
- component: () => import('src/pages/ItemType/Card/ItemTypeCard.vue'),
+ component: () => import('src/pages/Item/ItemType/Card/ItemTypeCard.vue'),
redirect: { name: 'ItemTypeSummary' },
children: [
{
@@ -28,7 +28,7 @@ export default {
title: 'summary',
},
component: () =>
- import('src/pages/ItemType/Card/ItemTypeSummary.vue'),
+ import('src/pages/Item/ItemType/Card/ItemTypeSummary.vue'),
},
{
name: 'ItemTypeBasicData',
@@ -38,7 +38,7 @@ export default {
icon: 'vn:settings',
},
component: () =>
- import('src/pages/ItemType/Card/ItemTypeBasicData.vue'),
+ import('src/pages/Item/ItemType/Card/ItemTypeBasicData.vue'),
},
{
path: 'log',
@@ -47,7 +47,8 @@ export default {
title: 'log',
icon: 'vn:History',
},
- component: () => import('src/pages/ItemType/Card/ItemTypeLog.vue'),
+ component: () =>
+ import('src/pages/Item/ItemType/Card/ItemTypeLog.vue'),
},
],
},
diff --git a/src/router/modules/worker.js b/src/router/modules/worker.js
index c2a9e668f..d1feff23d 100644
--- a/src/router/modules/worker.js
+++ b/src/router/modules/worker.js
@@ -87,6 +87,13 @@ export default {
meta: {
title: 'basicData',
icon: 'vn:settings',
+ acls: [
+ {
+ model: 'Worker',
+ props: 'updateAttributes',
+ accessType: 'WRITE',
+ },
+ ],
},
component: () => import('src/pages/Worker/Card/WorkerBasicData.vue'),
},
diff --git a/test/cypress/integration/parking/parkingList.spec.js b/test/cypress/integration/parking/parkingList.spec.js
index b78a660d1..f1efaa375 100644
--- a/test/cypress/integration/parking/parkingList.spec.js
+++ b/test/cypress/integration/parking/parkingList.spec.js
@@ -1,5 +1,6 @@
///
describe('ParkingList', () => {
+ const searchbar = '#searchbar input';
const firstCard = '.q-card:nth-child(1)';
const firstChipId =
':nth-child(1) > :nth-child(1) > .justify-between > .flex > .q-chip > .q-chip__content';
@@ -14,6 +15,7 @@ describe('ParkingList', () => {
});
it('should redirect on clicking a parking', () => {
+ cy.get(searchbar).type('{enter}');
cy.get(firstChipId)
.invoke('text')
.then((content) => {
@@ -24,6 +26,7 @@ describe('ParkingList', () => {
});
it('should open the details', () => {
+ cy.get(searchbar).type('{enter}');
cy.get(firstDetailBtn).click();
cy.get(summaryHeader).contains('Basic data');
});
diff --git a/test/cypress/integration/ticket/ticketExpedition.spec.js b/test/cypress/integration/ticket/ticketExpedition.spec.js
index 5eb2c1a2a..d4afd401f 100644
--- a/test/cypress/integration/ticket/ticketExpedition.spec.js
+++ b/test/cypress/integration/ticket/ticketExpedition.spec.js
@@ -11,15 +11,13 @@ describe('Ticket expedtion', () => {
it('should change the state', () => {
cy.visit('#/ticket/1/expedition');
- cy.intercept('GET', /\/api\/Expeditions\/filter/).as('expeditions');
- cy.intercept('POST', /\/api\/Expeditions\/crud/).as('crud');
-
- cy.wait('@expeditions');
+ cy.intercept('GET', /\/api\/Expeditions\/filter/).as('show');
+ cy.intercept('POST', /\/api\/ExpeditionStates\/addExpeditionState/).as('add');
+ cy.wait('@show');
cy.selectRows([1, 2]);
- cy.get('#subToolbar [aria-controls]:nth-child(1)').click();
- cy.get('.q-menu .q-item').contains('Perdida').click();
- cy.wait('@crud');
+ cy.selectOption('[data-cy="change-state"]', 'Perdida');
+ cy.wait('@add');
cy.get(`${tableContent} tr:nth-child(-n+2) ${stateTd}`).each(($el) => {
cy.wrap($el).contains('Perdida');
diff --git a/test/cypress/integration/worker/workerList.spec.js b/test/cypress/integration/worker/workerList.spec.js
index 8a8bea443..c1c37fd32 100644
--- a/test/cypress/integration/worker/workerList.spec.js
+++ b/test/cypress/integration/worker/workerList.spec.js
@@ -11,7 +11,7 @@ describe('WorkerList', () => {
it('should open the worker summary', () => {
cy.get(inputName).type('jessica{enter}');
cy.get(searchBtn).click();
- cy.intercept('GET', /\/api\/Workers\/\d+/).as('worker');
+ cy.intercept('GET', /\/api\/Workers\/summary+/).as('worker');
cy.wait('@worker').then(() =>
cy.get(descriptorTitle).should('include.text', 'Jessica')
);
diff --git a/test/cypress/integration/worker/workerLocker.spec.js b/test/cypress/integration/worker/workerLocker.spec.js
index 8a169dfb2..c222414fd 100644
--- a/test/cypress/integration/worker/workerLocker.spec.js
+++ b/test/cypress/integration/worker/workerLocker.spec.js
@@ -1,8 +1,7 @@
describe('WorkerLocker', () => {
const productionId = 49;
- const lockerCode = '2F';
- const input = '.q-card input';
- const thirdOpt = '[role="listbox"] .q-item:nth-child(1)';
+ const lockerCode = '4F';
+ const lockerSelect = '[data-cy="locker"]';
beforeEach(() => {
cy.viewport(1280, 720);
cy.login('productionBoss');
@@ -10,10 +9,8 @@ describe('WorkerLocker', () => {
});
it('should allocates a locker', () => {
- cy.get(input).click();
- cy.waitForElement('[role="listbox"]');
- cy.get(thirdOpt).click();
+ cy.selectOption(lockerSelect, lockerCode);
cy.saveCard();
- cy.get(input).invoke('val').should('eq', lockerCode);
+ cy.get(lockerSelect).invoke('val').should('eq', lockerCode);
});
});
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index c9b1a748e..76bdefd27 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -82,7 +82,7 @@ Cypress.Commands.add('getValue', (selector) => {
// Fill Inputs
Cypress.Commands.add('selectOption', (selector, option) => {
cy.waitForElement(selector);
- cy.get(selector).find('.q-select__dropdown-icon').click();
+ cy.get(selector).click();
cy.get('.q-menu .q-item').contains(option).click();
});
diff --git a/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js b/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js
new file mode 100644
index 000000000..ab1a47544
--- /dev/null
+++ b/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js
@@ -0,0 +1,120 @@
+import { vi, describe, expect, it, beforeAll, afterEach, beforeEach } from 'vitest';
+import { createWrapper, axios } from 'app/test/vitest/helper';
+import TicketAdvance from 'pages/Ticket/TicketAdvance.vue';
+import { Notify } from 'quasar';
+import { nextTick } from 'vue';
+
+describe('TicketAdvance', () => {
+ let wrapper;
+ let vm;
+
+ beforeAll(() => {
+ vi.spyOn(axios, 'get').mockImplementation(() => ({ data: [] }));
+ wrapper = createWrapper(TicketAdvance);
+ vm = wrapper.vm;
+ vi.spyOn(vm.vnTableRef, 'reload').mockImplementation(() => vi.fn());
+ vm.vnTableRef.value = { params: {} };
+ });
+ beforeEach(() => {
+ Notify.create = vi.fn();
+ });
+ afterEach(() => {
+ vi.clearAllMocks();
+ });
+
+ describe('requestComponentUpdate()', () => {
+ const mockTicket = {
+ futureId: 1,
+ futureClientFk: 1,
+ nickname: 'test',
+ futureAddressFk: 1,
+ futureAgencyModeFk: 1,
+ futureWarehouseFk: 1,
+ futureCompanyFk: 1,
+ landed: '2023-01-02',
+ zoneFk: 1,
+ };
+ const mockParams = {
+ clientFk: 1,
+ nickname: 'test',
+ agencyModeFk: 1,
+ addressFk: 1,
+ zoneFk: 1,
+ warehouseFk: 1,
+ companyFk: 1,
+ landed: '2023-01-02',
+ shipped: '2023-01-01',
+ isDeleted: false,
+ isWithoutNegatives: false,
+ newTicket: undefined,
+ keepPrice: true,
+ };
+ const queryResult = 'tickets/1/componentUpdate';
+
+ it('should return query and params when ticket has no landed', async () => {
+ vm.vnTableRef.params.dateToAdvance = '2023-01-01';
+ await nextTick();
+
+ const mockLanded = { landed: '2023-01-02', zoneFk: 1 };
+ vi.spyOn(vm, 'getLanded').mockResolvedValue(mockLanded);
+
+ const { query, params } = await vm.requestComponentUpdate(mockTicket, false);
+
+ expect(query).toBe(queryResult);
+ expect(params).toEqual(mockParams);
+ });
+
+ it('should return query and params when ticket has landed', async () => {
+ const { query, params } = await vm.requestComponentUpdate(mockTicket, false);
+
+ expect(query).toBe(queryResult);
+ expect(params).toEqual(mockParams);
+ });
+ });
+
+ describe('moveTicketsAdvance()', () => {
+ it('should move tickets and notify success', async () => {
+ const tickets = [
+ {
+ id: 1,
+ futureId: 2,
+ futureShipped: '2023-01-01',
+ shipped: '2023-01-02',
+ workerFk: 1,
+ },
+ {
+ id: 2,
+ futureId: 3,
+ futureShipped: '2023-01-01',
+ shipped: '2023-01-02',
+ workerFk: 1,
+ },
+ ];
+ vm.selectedTickets = tickets;
+ vi.spyOn(axios, 'post').mockResolvedValue({});
+ await vm.moveTicketsAdvance();
+
+ expect(axios.post).toHaveBeenCalledOnce('Tickets/merge', {
+ tickets: [
+ {
+ originId: 2,
+ destinationId: 1,
+ originShipped: '2023-01-01',
+ destinationShipped: '2023-01-02',
+ workerFk: 1,
+ },
+ {
+ originId: 3,
+ destinationId: 2,
+ originShipped: '2023-01-01',
+ destinationShipped: '2023-01-02',
+ workerFk: 1,
+ },
+ ],
+ });
+ expect(vm.vnTableRef.reload).toHaveBeenCalled();
+ expect(Notify.create).toHaveBeenCalled();
+ expect(vm.selectedTickets).toEqual([]);
+ });
+ });
+});
diff --git a/test/vitest/helper.js b/test/vitest/helper.js
index e201535ec..4bfae5dc8 100644
--- a/test/vitest/helper.js
+++ b/test/vitest/helper.js
@@ -70,8 +70,10 @@ class FormDataMock {
vi.fn();
}
}
+
global.FormData = FormDataMock;
global.URL = class URL {};
+global.Date.vnNew = () => new Date(Date.UTC(2001, 0, 1, 11));
export function createWrapper(component, options) {
const defaultOptions = {