From a0a6656a49eabd590d5a19cc85238f5657f061c3 Mon Sep 17 00:00:00 2001 From: richardtekula Date: Thu, 22 Jan 2026 07:46:50 +0100 Subject: [PATCH] feat: Hotfix Part1 - Backend support for company postal code, service tiers, timesheet naming - Add postal_code column to companies table - Add pricing_tiers column to services table for tiered pricing - Update timesheet upload to generate filename in format {firstname}-{lastname}-timesheet-{date} Co-Authored-By: Claude Opus 4.5 --- .../0008_add_company_postal_code.sql | 2 ++ .../0009_add_service_pricing_tiers.sql | 2 ++ src/db/schema.js | 2 ++ src/services/timesheet.service.js | 27 ++++++++++++++++++- 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/db/migrations/0008_add_company_postal_code.sql create mode 100644 src/db/migrations/0009_add_service_pricing_tiers.sql diff --git a/src/db/migrations/0008_add_company_postal_code.sql b/src/db/migrations/0008_add_company_postal_code.sql new file mode 100644 index 0000000..9c4adb0 --- /dev/null +++ b/src/db/migrations/0008_add_company_postal_code.sql @@ -0,0 +1,2 @@ +-- Add postal_code column to companies table +ALTER TABLE "companies" ADD COLUMN IF NOT EXISTS "postal_code" text; diff --git a/src/db/migrations/0009_add_service_pricing_tiers.sql b/src/db/migrations/0009_add_service_pricing_tiers.sql new file mode 100644 index 0000000..2fd4cdc --- /dev/null +++ b/src/db/migrations/0009_add_service_pricing_tiers.sql @@ -0,0 +1,2 @@ +-- Add pricing_tiers column to services table for tiered pricing +ALTER TABLE "services" ADD COLUMN IF NOT EXISTS "pricing_tiers" text; diff --git a/src/db/schema.js b/src/db/schema.js index 7df0cc0..1acb3b5 100644 --- a/src/db/schema.js +++ b/src/db/schema.js @@ -126,6 +126,7 @@ export const companies = pgTable('companies', { description: text('description'), address: text('address'), city: text('city'), + postalCode: text('postal_code'), country: text('country'), phone: text('phone'), email: text('email'), @@ -320,6 +321,7 @@ export const services = pgTable('services', { id: uuid('id').primaryKey().defaultRandom(), name: text('name').notNull(), price: text('price').notNull(), // stored as text for flexibility with decimal + pricingTiers: text('pricing_tiers'), // JSON array of tiers: [{tier: "Silver", price: "€149"}, ...] description: text('description'), createdBy: uuid('created_by').references(() => users.id, { onDelete: 'set null' }), createdAt: timestamp('created_at').defaultNow().notNull(), diff --git a/src/services/timesheet.service.js b/src/services/timesheet.service.js index 98a1033..92ba4d4 100644 --- a/src/services/timesheet.service.js +++ b/src/services/timesheet.service.js @@ -76,16 +76,41 @@ const safeUnlink = async (filePath) => { } }; +const generateTimesheetFileName = (firstName, lastName, year, month, fileExt) => { + const cleanFirstName = (firstName || 'user').toLowerCase().replace(/\s+/g, '-'); + const cleanLastName = (lastName || '').toLowerCase().replace(/\s+/g, '-'); + const monthStr = String(month).padStart(2, '0'); + const namePrefix = cleanLastName ? `${cleanFirstName}-${cleanLastName}` : cleanFirstName; + return `${namePrefix}-timesheet-${year}-${monthStr}${fileExt}`; +}; + export const uploadTimesheet = async ({ userId, year, month, file }) => { if (!file) { throw new BadRequestError('Súbor nebol nahraný'); } + // Fetch user info for filename generation + const [user] = await db + .select({ firstName: users.firstName, lastName: users.lastName }) + .from(users) + .where(eq(users.id, userId)) + .limit(1); + const parsedYear = parseInt(year); const parsedMonth = parseInt(month); const fileType = detectFileType(file.mimetype); + const fileExt = path.extname(file.originalname); const { folder, filename, filePath } = buildDestinationPath(userId, parsedYear, parsedMonth, file.originalname); + // Generate user-friendly filename + const displayFileName = generateTimesheetFileName( + user?.firstName, + user?.lastName, + parsedYear, + parsedMonth, + fileExt + ); + await fs.mkdir(folder, { recursive: true }); try { @@ -95,7 +120,7 @@ export const uploadTimesheet = async ({ userId, year, month, file }) => { .insert(timesheets) .values({ userId, - fileName: file.originalname, + fileName: displayFileName, filePath, fileType, fileSize: file.size,