diff --git a/backend/src/routes/functions.ts b/backend/src/routes/functions.ts index e94917d..dc96bb7 100644 --- a/backend/src/routes/functions.ts +++ b/backend/src/routes/functions.ts @@ -100,20 +100,25 @@ export default async function functionRoutes(server: FastifyInstance) { server.get('/functions/check-zip/:zip', async (req, reply) => { const { zip } = req.params as { zip: string } + const normalizedZip = String(zip || "").replace(/\D/g, "") - if (!zip) { - return reply.code(400).send({ error: 'ZIP is required' }) + if (normalizedZip.length !== 5) { + return reply.code(400).send({ error: 'ZIP must contain exactly 5 digits' }) } try { - //@ts-ignore - const data = await server.db.select().from(citys).where(eq(citys.zip,zip)) + const data = await server.db + .select() + .from(citys) + .where(eq(citys.zip, Number(normalizedZip))) - if (!data) { + if (!data.length) { return reply.code(404).send({ error: 'ZIP not found' }) } + const city = data[0] + //districtMap const bundeslaender = [ { code: 'DE-BW', name: 'Baden-Württemberg' }, @@ -137,9 +142,8 @@ export default async function functionRoutes(server: FastifyInstance) { return reply.send({ - ...data, - //@ts-ignore - state_code: bundeslaender.find(i => i.name === data.countryName) + ...city, + state_code: bundeslaender.find(i => i.name === city.countryName)?.code || null }) } catch (err) { console.log(err) diff --git a/frontend/composables/useFunctions.js b/frontend/composables/useFunctions.js index d303e1d..51713e6 100644 --- a/frontend/composables/useFunctions.js +++ b/frontend/composables/useFunctions.js @@ -50,12 +50,23 @@ export const useFunctions = () => { } const useZipCheck = async (zip) => { - const returnData = await useNuxtApp().$api(`/api/functions/check-zip/${zip}`, { - method: "GET", - }) - - return returnData + const normalizedZip = String(zip || "").replace(/\D/g, "") + if (!normalizedZip || normalizedZip.length > 5) { + return null + } + const lookupZip = normalizedZip.padStart(5, "0") + try { + const data = await useNuxtApp().$api(`/api/functions/check-zip/${lookupZip}`, { + method: "GET", + }) + return { + ...data, + zip: String(data?.zip ?? lookupZip).replace(/\D/g, "").padStart(5, "0") + } + } catch (e) { + return null + } } @@ -75,5 +86,5 @@ export const useFunctions = () => { } - return {getWorkingTimesEvaluationData, useNextNumber, useCreateTicket, useBankingGenerateLink, useZipCheck, useBankingCheckInstitutions, useBankingListRequisitions, useCreatePDF,useGetInvoiceData, useSendTelegramNotification} -} \ No newline at end of file + return {getWorkingTimesEvaluationData, useNextNumber, useBankingGenerateLink, useZipCheck, useBankingCheckInstitutions, useBankingListRequisitions, useCreatePDF} +} diff --git a/frontend/pages/createDocument/edit/[[id]].vue b/frontend/pages/createDocument/edit/[[id]].vue index 33bc9ae..41eb974 100644 --- a/frontend/pages/createDocument/edit/[[id]].vue +++ b/frontend/pages/createDocument/edit/[[id]].vue @@ -633,6 +633,27 @@ const removePosition = (id) => { } +const normalizeZip = (value) => String(value || "").replace(/\D/g, "") + +const sanitizeAddressZipInput = () => { + itemInfo.value.address.zip = normalizeZip(itemInfo.value.address.zip) +} + +const checkAddressZip = async () => { + const zip = normalizeZip(itemInfo.value.address.zip) + itemInfo.value.address.zip = zip + + if (![4, 5].includes(zip.length)) return + + const zipData = await useFunctions().useZipCheck(zip) + if (zipData?.zip) { + itemInfo.value.address.zip = zipData.zip + } + if (zipData?.short) { + itemInfo.value.address.city = zipData.short + } +} + const findDocumentErrors = computed(() => { let errors = [] @@ -640,15 +661,15 @@ const findDocumentErrors = computed(() => { if (itemInfo.value.contact === null) errors.push({message: "Es ist kein Kontakt ausgewählt", type: "info"}) if (itemInfo.value.letterhead === null) errors.push({message: "Es ist kein Briefpapier ausgewählt", type: "breaking"}) if (itemInfo.value.created_by === null || !itemInfo.value.created_by) errors.push({message: "Es ist kein Mitarbeiter ausgewählt", type: "breaking"}) - if (itemInfo.value.address.street === null) errors.push({ + if (!itemInfo.value.address.street) errors.push({ message: "Es ist keine Straße im Adressat angegeben", type: "breaking" }) - if (itemInfo.value.address.zip === null) errors.push({ + if (!itemInfo.value.address.zip) errors.push({ message: "Es ist keine Postleitzahl im Adressat angegeben", type: "breaking" }) - if (itemInfo.value.address.city === null) errors.push({ + if (!itemInfo.value.address.city) errors.push({ message: "Es ist keine Stadt im Adressat angegeben", type: "breaking" }) @@ -1893,6 +1914,11 @@ const setRowData = async (row, service = {sellingPriceComposed: {}}, product = { @@ -3177,4 +3203,4 @@ td { height: 80vh; } - \ No newline at end of file + diff --git a/frontend/pages/staff/profiles/[id].vue b/frontend/pages/staff/profiles/[id].vue index 508f94c..1735daa 100644 --- a/frontend/pages/staff/profiles/[id].vue +++ b/frontend/pages/staff/profiles/[id].vue @@ -109,8 +109,11 @@ function recalculateWeeklyHours() { const checkZip = async () => { const zipData = await useFunctions().useZipCheck(profile.value.address_zip) - profile.value.address_city = zipData.short - profile.value.state_code = zipData.state_code + if (zipData) { + profile.value.address_zip = zipData.zip || profile.value.address_zip + profile.value.address_city = zipData.short + profile.value.state_code = zipData.state_code + } } onMounted(fetchProfile) @@ -314,5 +317,3 @@ onMounted(fetchProfile) - - diff --git a/frontend/stores/data.js b/frontend/stores/data.js index 738c6b5..1206ac1 100644 --- a/frontend/stores/data.js +++ b/frontend/stores/data.js @@ -333,10 +333,14 @@ export const useDataStore = defineStore('data', () => { { key: "infoData.zip", label: "Postleitzahl", - inputType: "number", + inputType: "text", inputChangeFunction: async function (row) { - if(row.infoData.zip) { - row.infoData.city = (await useFunctions().useZipCheck(row.infoData.zip)).short + const zip = String(row.infoData.zip || "").replace(/\D/g, "") + row.infoData.zip = zip + if ([4, 5].includes(zip.length)) { + const zipData = await useFunctions().useZipCheck(zip) + row.infoData.zip = zipData?.zip || row.infoData.zip + row.infoData.city = zipData?.short || row.infoData.city } }, disabledInTable: true, @@ -1291,11 +1295,15 @@ export const useDataStore = defineStore('data', () => { { key: "infoData.zip", label: "Postleitzahl", - inputType: "number", + inputType: "text", disabledInTable: true, inputChangeFunction: async function (row) { - if(row.infoData.zip) { - row.infoData.city = (await useFunctions().useZipCheck(row.infoData.zip)).short + const zip = String(row.infoData.zip || "").replace(/\D/g, "") + row.infoData.zip = zip + if ([4, 5].includes(zip.length)) { + const zipData = await useFunctions().useZipCheck(zip) + row.infoData.zip = zipData?.zip || row.infoData.zip + row.infoData.city = zipData?.short || row.infoData.city } }, }, @@ -1474,12 +1482,16 @@ export const useDataStore = defineStore('data', () => { { key: "info_data.zip", label: "Postleitzahl", - inputType: "number", + inputType: "text", disabledInTable: true, inputColumn: "Ort", inputChangeFunction: async function (row) { - if(row.infoData.zip) { - row.infoData.city = (await useFunctions().useZipCheck(row.infoData.zip)).short + const zip = String(row.info_data.zip || "").replace(/\D/g, "") + row.info_data.zip = zip + if ([4, 5].includes(zip.length)) { + const zipData = await useFunctions().useZipCheck(zip) + row.info_data.zip = zipData?.zip || row.info_data.zip + row.info_data.city = zipData?.short || row.info_data.city } }, }, @@ -2617,4 +2629,4 @@ export const useDataStore = defineStore('data', () => { dataTypes, documentTypesForCreation, } -}) \ No newline at end of file +})