feat: Add IČO and DIČ fields to companies

- Add ico and dic columns to companies table schema
- Add validation for ico and dic in createCompanySchema and updateCompanySchema
- Update company.service.js to include ico and dic in all CRUD operations
- Include migration file for database changes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
richardtekula
2026-01-30 07:47:22 +01:00
parent 159c22da16
commit 09f4c72acb
6 changed files with 3669 additions and 2 deletions

View File

@@ -0,0 +1,19 @@
ALTER TYPE "public"."role" ADD VALUE 'team_leader' BEFORE 'member';--> statement-breakpoint
ALTER TABLE "companies" ADD COLUMN "postal_code" text;--> statement-breakpoint
ALTER TABLE "companies" ADD COLUMN "ico" text;--> statement-breakpoint
ALTER TABLE "companies" ADD COLUMN "dic" text;--> statement-breakpoint
ALTER TABLE "kurzy" ADD COLUMN "farba" varchar(20);--> statement-breakpoint
ALTER TABLE "kurzy" ADD COLUMN "datum_od" date;--> statement-breakpoint
ALTER TABLE "kurzy" ADD COLUMN "datum_do" date;--> statement-breakpoint
ALTER TABLE "services" ADD COLUMN "pricing_tiers" text;--> statement-breakpoint
ALTER TABLE "time_entries" ADD COLUMN "paused_at" timestamp;--> statement-breakpoint
ALTER TABLE "time_entries" ADD COLUMN "paused_duration" integer DEFAULT 0 NOT NULL;--> statement-breakpoint
ALTER TABLE "ucastnici" ADD COLUMN "firma_ico" varchar(20);--> statement-breakpoint
ALTER TABLE "ucastnici" ADD COLUMN "firma_dic" varchar(20);--> statement-breakpoint
ALTER TABLE "ucastnici" ADD COLUMN "firma_ic_dph" varchar(25);--> statement-breakpoint
ALTER TABLE "ucastnici" ADD COLUMN "firma_sidlo" text;--> statement-breakpoint
ALTER TABLE "ucastnici" ADD COLUMN "needs_followup" boolean DEFAULT false NOT NULL;--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "last_seen" timestamp;--> statement-breakpoint
ALTER TABLE "registracie" DROP COLUMN "datum_od";--> statement-breakpoint
ALTER TABLE "registracie" DROP COLUMN "datum_do";--> statement-breakpoint
ALTER TABLE "registracie" DROP COLUMN "pocet_ucastnikov";

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,13 @@
"when": 1768990516243, "when": 1768990516243,
"tag": "0001_living_natasha_romanoff", "tag": "0001_living_natasha_romanoff",
"breakpoints": true "breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1769754011560,
"tag": "0002_soft_black_tarantula",
"breakpoints": true
} }
] ]
} }

View File

@@ -132,6 +132,8 @@ export const companies = pgTable('companies', {
phone: text('phone'), phone: text('phone'),
email: text('email'), email: text('email'),
website: text('website'), website: text('website'),
ico: text('ico'), // IČO - Company ID number
dic: text('dic'), // DIČ - Tax ID number
status: companyStatusEnum('status').default('registered').notNull(), // stav firmy status: companyStatusEnum('status').default('registered').notNull(), // stav firmy
createdBy: uuid('created_by').references(() => users.id, { onDelete: 'set null' }), createdBy: uuid('created_by').references(() => users.id, { onDelete: 'set null' }),
createdAt: timestamp('created_at').defaultNow().notNull(), createdAt: timestamp('created_at').defaultNow().notNull(),

View File

@@ -37,6 +37,8 @@ export const getAllCompanies = async (searchTerm = null, userId = null, userRole
phone: companies.phone, phone: companies.phone,
email: companies.email, email: companies.email,
website: companies.website, website: companies.website,
ico: companies.ico,
dic: companies.dic,
status: companies.status, status: companies.status,
createdBy: companies.createdBy, createdBy: companies.createdBy,
createdAt: companies.createdAt, createdAt: companies.createdAt,
@@ -111,6 +113,8 @@ export const getCompanyById = async (companyId) => {
phone: companies.phone, phone: companies.phone,
email: companies.email, email: companies.email,
website: companies.website, website: companies.website,
ico: companies.ico,
dic: companies.dic,
status: companies.status, status: companies.status,
createdBy: companies.createdBy, createdBy: companies.createdBy,
createdAt: companies.createdAt, createdAt: companies.createdAt,
@@ -136,7 +140,7 @@ export const getCompanyById = async (companyId) => {
* Create new company * Create new company
*/ */
export const createCompany = async (userId, data, auditContext = null) => { export const createCompany = async (userId, data, auditContext = null) => {
const { name, description, address, city, postalCode, country, phone, email, website, status } = data; const { name, description, address, city, postalCode, country, phone, email, website, ico, dic, status } = data;
// Check if company with same name already exists // Check if company with same name already exists
const [existing] = await db const [existing] = await db
@@ -161,6 +165,8 @@ export const createCompany = async (userId, data, auditContext = null) => {
phone: phone || null, phone: phone || null,
email: email || null, email: email || null,
website: website || null, website: website || null,
ico: ico || null,
dic: dic || null,
status: status || 'registered', status: status || 'registered',
createdBy: userId, createdBy: userId,
}) })
@@ -187,7 +193,7 @@ export const createCompany = async (userId, data, auditContext = null) => {
export const updateCompany = async (companyId, data, auditContext = null) => { export const updateCompany = async (companyId, data, auditContext = null) => {
const company = await getCompanyById(companyId); const company = await getCompanyById(companyId);
const { name, description, address, city, postalCode, country, phone, email, website, status } = data; const { name, description, address, city, postalCode, country, phone, email, website, ico, dic, status } = data;
// If name is being changed, check for duplicates // If name is being changed, check for duplicates
if (name && name !== company.name) { if (name && name !== company.name) {
@@ -214,6 +220,8 @@ export const updateCompany = async (companyId, data, auditContext = null) => {
phone: phone !== undefined ? phone : company.phone, phone: phone !== undefined ? phone : company.phone,
email: email !== undefined ? email : company.email, email: email !== undefined ? email : company.email,
website: website !== undefined ? website : company.website, website: website !== undefined ? website : company.website,
ico: ico !== undefined ? ico : company.ico,
dic: dic !== undefined ? dic : company.dic,
status: status !== undefined ? status : company.status, status: status !== undefined ? status : company.status,
updatedAt: new Date(), updatedAt: new Date(),
}) })

View File

@@ -16,6 +16,8 @@ export const createCompanySchema = z.object({
phone: z.string().max(50).optional(), phone: z.string().max(50).optional(),
email: z.string().email('Neplatný formát emailu').max(255).optional().or(z.literal('')), email: z.string().email('Neplatný formát emailu').max(255).optional().or(z.literal('')),
website: z.string().url('Neplatný formát URL').max(255).optional().or(z.literal('')), website: z.string().url('Neplatný formát URL').max(255).optional().or(z.literal('')),
ico: z.string().max(20).optional().or(z.literal('')),
dic: z.string().max(20).optional().or(z.literal('')),
status: z.enum(['registered', 'lead', 'customer', 'inactive']).optional(), status: z.enum(['registered', 'lead', 'customer', 'inactive']).optional(),
}); });
@@ -29,6 +31,8 @@ export const updateCompanySchema = z.object({
phone: z.string().max(50).optional(), phone: z.string().max(50).optional(),
email: z.string().email('Neplatný formát emailu').max(255).optional().or(z.literal('')), email: z.string().email('Neplatný formát emailu').max(255).optional().or(z.literal('')),
website: z.string().url('Neplatný formát URL').max(255).optional().or(z.literal('')), website: z.string().url('Neplatný formát URL').max(255).optional().or(z.literal('')),
ico: z.string().max(20).optional().or(z.literal('').or(z.null())),
dic: z.string().max(20).optional().or(z.literal('').or(z.null())),
status: z.enum(['registered', 'lead', 'customer', 'inactive']).optional(), status: z.enum(['registered', 'lead', 'customer', 'inactive']).optional(),
}); });