diff --git a/src/components/CrudModel.vue b/src/components/CrudModel.vue
index 93a2ac96a7..8c4f70f3b9 100644
--- a/src/components/CrudModel.vue
+++ b/src/components/CrudModel.vue
@@ -184,8 +184,11 @@ async function saveChanges(data) {
if ($props.beforeSaveFn) {
changes = await $props.beforeSaveFn(changes, getChanges);
}
-
try {
+ if (changes?.creates?.length === 0 && changes?.updates?.length === 0) {
+ return;
+ }
+
await axios.post($props.saveUrl || $props.url + '/crud', changes);
} finally {
isLoading.value = false;
diff --git a/src/components/__tests__/CrudModel.spec.js b/src/components/__tests__/CrudModel.spec.js
index e0afd30adf..f6c93e0d5a 100644
--- a/src/components/__tests__/CrudModel.spec.js
+++ b/src/components/__tests__/CrudModel.spec.js
@@ -30,8 +30,8 @@ describe('CrudModel', () => {
saveFn: '',
},
});
- wrapper=wrapper.wrapper;
- vm=wrapper.vm;
+ wrapper = wrapper.wrapper;
+ vm = wrapper.vm;
});
beforeEach(() => {
@@ -143,14 +143,14 @@ describe('CrudModel', () => {
});
it('should return true if object is empty', async () => {
- dummyObj ={};
- result = vm.isEmpty(dummyObj);
+ dummyObj = {};
+ result = vm.isEmpty(dummyObj);
expect(result).toBe(true);
});
it('should return false if object is not empty', async () => {
- dummyObj = {a:1, b:2, c:3};
+ dummyObj = { a: 1, b: 2, c: 3 };
result = vm.isEmpty(dummyObj);
expect(result).toBe(false);
@@ -158,29 +158,31 @@ describe('CrudModel', () => {
it('should return true if array is empty', async () => {
dummyArray = [];
- result = vm.isEmpty(dummyArray);
+ result = vm.isEmpty(dummyArray);
expect(result).toBe(true);
});
-
+
it('should return false if array is not empty', async () => {
- dummyArray = [1,2,3];
+ dummyArray = [1, 2, 3];
result = vm.isEmpty(dummyArray);
expect(result).toBe(false);
- })
+ });
});
describe('resetData()', () => {
it('should add $index to elements in data[] and sets originalData and formData with data', async () => {
- data = [{
- name: 'Tony',
- lastName: 'Stark',
- age: 42,
- }];
+ data = [
+ {
+ name: 'Tony',
+ lastName: 'Stark',
+ age: 42,
+ },
+ ];
vm.resetData(data);
-
+
expect(vm.originalData).toEqual(data);
expect(vm.originalData[0].$index).toEqual(0);
expect(vm.formData).toEqual(data);
@@ -200,7 +202,7 @@ describe('CrudModel', () => {
lastName: 'Stark',
age: 42,
};
-
+
vm.resetData(data);
expect(vm.originalData).toEqual(data);
@@ -210,17 +212,19 @@ describe('CrudModel', () => {
});
describe('saveChanges()', () => {
- data = [{
- name: 'Tony',
- lastName: 'Stark',
- age: 42,
- }];
+ data = [
+ {
+ name: 'Tony',
+ lastName: 'Stark',
+ age: 42,
+ },
+ ];
it('should call saveFn if exists', async () => {
await wrapper.setProps({ saveFn: vi.fn() });
vm.saveChanges(data);
-
+
expect(vm.saveFn).toHaveBeenCalledOnce();
expect(vm.isLoading).toBe(false);
expect(vm.hasChanges).toBe(false);
@@ -229,13 +233,15 @@ describe('CrudModel', () => {
});
it("should use default url if there's not saveFn", async () => {
- const postMock =vi.spyOn(axios, 'post');
-
- vm.formData = [{
- name: 'Bruce',
- lastName: 'Wayne',
- age: 45,
- }]
+ const postMock = vi.spyOn(axios, 'post');
+
+ vm.formData = [
+ {
+ name: 'Bruce',
+ lastName: 'Wayne',
+ age: 45,
+ },
+ ];
await vm.saveChanges(data);
diff --git a/src/pages/Customer/Card/CustomerSummary.vue b/src/pages/Customer/Card/CustomerSummary.vue
index 324da0771a..c98bf1ffbb 100644
--- a/src/pages/Customer/Card/CustomerSummary.vue
+++ b/src/pages/Customer/Card/CustomerSummary.vue
@@ -325,7 +325,7 @@ const sumRisk = ({ clientRisks }) => {
-
+
diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue
index bb6f4442ba..09c7e714c5 100644
--- a/src/pages/Customer/components/CustomerSummaryTable.vue
+++ b/src/pages/Customer/components/CustomerSummaryTable.vue
@@ -20,7 +20,12 @@ const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const { viewSummary } = useSummaryDialog();
-
+const $props = defineProps({
+ id: {
+ type: Number,
+ default: null,
+ },
+});
const filter = {
include: [
{
@@ -43,7 +48,7 @@ const filter = {
},
},
],
- where: { clientFk: route.params.id },
+ where: { clientFk: $props.id ?? route.params.id },
order: ['shipped DESC', 'id'],
limit: 30,
};
diff --git a/src/pages/Item/Card/ItemDiary.vue b/src/pages/Item/Card/ItemDiary.vue
index 83cd562a0a..f839c1f71d 100644
--- a/src/pages/Item/Card/ItemDiary.vue
+++ b/src/pages/Item/Card/ItemDiary.vue
@@ -158,15 +158,10 @@ const getBadgeAttrs = (_date) => {
const scrollToToday = async () => {
await nextTick();
- const todayCell = document.querySelector(`td[data-date="${today.toISOString()}"]`);
- if (todayCell) {
- todayCell.scrollIntoView({ behavior: 'smooth', block: 'center' });
- }
-};
-
-const formatDateForAttribute = (dateValue) => {
- if (dateValue instanceof Date) return date.formatDate(dateValue, 'YYYY-MM-DD');
- return dateValue;
+ const todayCell = document.querySelector(
+ `td[data-date="${date.formatDate(today, 'YYYY-MM-DD')}"]`,
+ );
+ if (todayCell) todayCell.scrollIntoView({ behavior: 'smooth', block: 'center' });
};
async function updateWarehouse(warehouseFk) {
@@ -242,7 +237,7 @@ async function updateWarehouse(warehouseFk) {
-
+
{
+onMounted(async () => {
stateStore.rightDrawer = true;
checkOrderConfirmation();
+
+ if (
+ arrayData.store.userParams &&
+ Object.keys(arrayData.store.userParams).some((key) => !key.startsWith('order'))
+ ) {
+ await arrayData.fetch({});
+ }
});
+onUnmounted(() => {
+ arrayData.destroy();
+});
+
+function exprBuilder(param, value) {
+ switch (param) {
+ case 'categoryFk':
+ case 'typeFk':
+ return { [param]: value };
+ case 'search':
+ if (/^\d+$/.test(value)) return { 'i.id': value };
+ else return { 'i.name': { like: `%${value}%` } };
+ }
+}
+
async function checkOrderConfirmation() {
const response = await axios.get(`Orders/${route.params.id}`);
if (response.data.isConfirmed === 1) {
@@ -96,6 +121,7 @@ watch(
:tag-value="tagValue"
:tags="tags"
:initial-catalog-params="catalogParams"
+ :arrayData
/>
diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue
index 76e6089834..d16a920179 100644
--- a/src/pages/Order/Card/OrderCatalogFilter.vue
+++ b/src/pages/Order/Card/OrderCatalogFilter.vue
@@ -24,6 +24,10 @@ const props = defineProps({
type: Array,
required: true,
},
+ arrayData: {
+ type: Object,
+ required: true,
+ },
});
const { t } = useI18n();
@@ -74,17 +78,6 @@ const loadTypes = async (id) => {
typeList.value = data;
};
-function exprBuilder(param, value) {
- switch (param) {
- case 'categoryFk':
- case 'typeFk':
- return { [param]: value };
- case 'search':
- if (/^\d+$/.test(value)) return { 'i.id': value };
- else return { 'i.name': { like: `%${value}%` } };
- }
-}
-
const applyTags = (tagInfo, params, search) => {
if (!tagInfo || !tagInfo.values.length) {
params.tagGroups = null;
@@ -152,9 +145,8 @@ function addOrder(value, field, params) {
:data-key="props.dataKey"
:hidden-tags="['filter', 'orderFk', 'orderBy']"
:unremovable-params="['orderFk', 'orderBy']"
- :expr-builder="exprBuilder"
:custom-tags="['tagGroups', 'categoryFk']"
- :redirect="false"
+ :arrayData
>
diff --git a/src/pages/Ticket/Card/TicketService.vue b/src/pages/Ticket/Card/TicketService.vue
index 6ce69a6aa4..1bd1548a48 100644
--- a/src/pages/Ticket/Card/TicketService.vue
+++ b/src/pages/Ticket/Card/TicketService.vue
@@ -121,6 +121,50 @@ async function handleSave() {
isSaving.value = false;
}
}
+function validateFields(item) {
+ // Only validate fields that are being updated
+ const shouldExist = (field) => !isUpdate || field in item;
+
+ if (!shouldExist('ticketServiceTypeFk') && !item.ticketServiceTypeFk) {
+ notify('Description is required', 'negative');
+ return false;
+ }
+
+ if (!shouldExist('quantity') && (!item.quantity || item.quantity <= 0)) {
+ notify('Quantity must be greater than 0', 'negative');
+ return false;
+ }
+
+ if (!shouldExist('price') && (!item.price || item.price < 0)) {
+ notify('Price must be valid', 'negative');
+ return false;
+ }
+
+ return true;
+}
+
+function beforeSave(data) {
+ const { creates = [], updates = [] } = data;
+ const validData = { creates: [], updates: [] };
+
+ // Validate creates
+ if (creates.length) {
+ for (const create of creates) {
+ create.ticketFk = route.params.id;
+ if (validateFields(create)) {
+ validData.creates.push(create);
+ }
+ }
+ }
+
+ // Validate updates
+ if (updates.length) {
+ for (const update of updates) {
+ validData.updates.push(update);
+ }
+ }
+ return validData;
+}
@@ -141,6 +185,7 @@ async function handleSave() {
v-model:selected="selected"
:order="['description ASC']"
:default-remove="false"
+ :beforeSaveFn="beforeSave"
>
@@ -196,6 +243,7 @@ async function handleSave() {
:label="col.label"
v-model.number="row.price"
type="number"
+ :required="true"
min="0"
@keyup.enter="handleSave"
/>
diff --git a/src/pages/Worker/Card/WorkerBasicData.vue b/src/pages/Worker/Card/WorkerBasicData.vue
index cf43412aff..ace2209838 100644
--- a/src/pages/Worker/Card/WorkerBasicData.vue
+++ b/src/pages/Worker/Card/WorkerBasicData.vue
@@ -1,5 +1,5 @@