diff --git a/apply_db_fixes.js b/apply_db_fixes.js new file mode 100644 index 0000000..9afbc33 --- /dev/null +++ b/apply_db_fixes.js @@ -0,0 +1,52 @@ +const { Pool } = require('pg'); +const { config } = require('./src/config/env'); + +const connectionString = config.DATABASE_PUBLIC_URL || config.DATABASE_URL; + +const pool = new Pool({ + connectionString, + ssl: connectionString.includes('localhost') ? false : { rejectUnauthorized: false } +}); + +async function applyFixes() { + try { + const client = await pool.connect(); + console.log('Connected to DB'); + + console.log('Adding "Others" to ConstructionStage enum...'); + try { + await client.query(`ALTER TYPE daily_updates_constructionstage_enum ADD VALUE 'Others'`); + console.log('Added "Others" successfully'); + } catch (e) { + if (e.code === '42710') { // duplicate_object + console.log('"Others" already exists in enum'); + } else { + throw e; + } + } + + console.log('Adding "work_completed" column to daily_updates...'); + try { + // Since some columns in this table are CamelCase and some are snake_case, + // let's check what the schema expects. + // Schema has: @map("work_completed") + // So column should be "work_completed" (lowercase snake_case) + await client.query(`ALTER TABLE daily_updates ADD COLUMN work_completed VARCHAR(255)`); + console.log('Added "work_completed" successfully'); + } catch (e) { + if (e.code === '42701') { // duplicate_column + console.log('"work_completed" already exists'); + } else { + throw e; + } + } + + client.release(); + } catch (err) { + console.error('Error applying fixes:', err); + } finally { + await pool.end(); + } +} + +applyFixes(); diff --git a/debug_db_columns.js b/debug_db_columns.js new file mode 100644 index 0000000..5677438 --- /dev/null +++ b/debug_db_columns.js @@ -0,0 +1,40 @@ +const { Pool } = require('pg'); +const { config } = require('./src/config/env'); + +const connectionString = config.DATABASE_PUBLIC_URL || config.DATABASE_URL; + +const pool = new Pool({ + connectionString, + ssl: connectionString.includes('localhost') ? false : { rejectUnauthorized: false } +}); + +async function checkColumns() { + try { + const client = await pool.connect(); + console.log('Connected to DB'); + + const tables = ['daily_updates', 'projects', 'supervisors', 'users']; + + for (const table of tables) { + console.log(`\nTable: ${table}`); + const res = await client.query(` + SELECT column_name, data_type + FROM information_schema.columns + WHERE table_name = $1 + ORDER BY ordinal_position; + `, [table]); + + res.rows.forEach(row => { + console.log(`- ${row.column_name}: ${row.data_type}`); + }); + } + + client.release(); + } catch (err) { + console.error('Error:', err); + } finally { + await pool.end(); + } +} + +checkColumns(); diff --git a/debug_db_enums.js b/debug_db_enums.js new file mode 100644 index 0000000..804671d --- /dev/null +++ b/debug_db_enums.js @@ -0,0 +1,47 @@ +const { Pool } = require('pg'); +const { config } = require('./src/config/env'); + +const connectionString = config.DATABASE_PUBLIC_URL || config.DATABASE_URL; + +const pool = new Pool({ + connectionString, + ssl: connectionString.includes('localhost') ? false : { rejectUnauthorized: false } +}); + +async function checkEnums() { + try { + const client = await pool.connect(); + console.log('Connected to DB'); + + const enums = [ + 'daily_updates_constructionstage_enum', + 'daily_updates_status_enum', + 'users_role_enum', + 'projects_initialstatus_enum', + 'supervisors_status_enum' + ]; + + for (const enumName of enums) { + console.log(`\nEnum: ${enumName}`); + const res = await client.query(` + SELECT enumlabel + FROM pg_enum + JOIN pg_type ON pg_enum.enumtypid = pg_type.oid + WHERE pg_type.typname = $1 + ORDER BY enumsortorder; + `, [enumName]); + + res.rows.forEach(row => { + console.log(`- ${row.enumlabel}`); + }); + } + + client.release(); + } catch (err) { + console.error('Error:', err); + } finally { + await pool.end(); + } +} + +checkEnums(); diff --git a/debug_db_exact_columns.js b/debug_db_exact_columns.js new file mode 100644 index 0000000..439fdeb --- /dev/null +++ b/debug_db_exact_columns.js @@ -0,0 +1,37 @@ +const { Pool } = require('pg'); +const { config } = require('./src/config/env'); + +const connectionString = config.DATABASE_PUBLIC_URL || config.DATABASE_URL; + +const pool = new Pool({ + connectionString, + ssl: connectionString.includes('localhost') ? false : { rejectUnauthorized: false } +}); + +async function checkExactColumns() { + try { + const client = await pool.connect(); + const tables = ['daily_updates', 'projects', 'supervisors']; + + for (const table of tables) { + console.log(`\nExact Columns for ${table}:`); + const res = await client.query(` + SELECT column_name + FROM information_schema.columns + WHERE table_name = $1 + `, [table]); + + res.rows.forEach(row => { + console.log(`'${row.column_name}'`); + }); + } + + client.release(); + } catch (err) { + console.error('Error:', err); + } finally { + await pool.end(); + } +} + +checkExactColumns(); diff --git a/diff.sql b/diff.sql index 1423cdd..bde74c5 100644 Binary files a/diff.sql and b/diff.sql differ diff --git a/list_tables.js b/list_tables.js new file mode 100644 index 0000000..f31427b --- /dev/null +++ b/list_tables.js @@ -0,0 +1,24 @@ +const { Pool } = require('pg'); +const { config } = require('./src/config/env'); + +const connectionString = config.DATABASE_PUBLIC_URL || config.DATABASE_URL; + +const pool = new Pool({ + connectionString, + ssl: connectionString.includes('localhost') ? false : { rejectUnauthorized: false } +}); + +async function listTables() { + try { + const client = await pool.connect(); + const res = await client.query("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"); + res.rows.forEach(r => console.log(r.table_name)); + client.release(); + } catch (err) { + console.error(err); + } finally { + await pool.end(); + } +} + +listTables(); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9a33bf9..dae1b84 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -52,6 +52,7 @@ enum ConstructionStage { Interior_Walls @map("Interior Walls") Painting Finishing + Others @@map("daily_updates_constructionstage_enum") } @@ -300,6 +301,7 @@ model DailyUpdate { dailyUpdateId String @id @default(uuid()) @db.Uuid constructionStage ConstructionStage @default(Foundation) description String? @db.Text + workCompleted String? @map("work_completed") @db.VarChar(255) rawMaterials Json? @default("[]") imageUrl String? @map("image_url") @db.VarChar(500) imageName String? @map("image_name") @db.VarChar(255) diff --git a/src/modules/daily-updates/daily-updates.controller.ts b/src/modules/daily-updates/daily-updates.controller.ts index e37538e..3cf2080 100644 --- a/src/modules/daily-updates/daily-updates.controller.ts +++ b/src/modules/daily-updates/daily-updates.controller.ts @@ -113,6 +113,7 @@ export const createDailyUpdate = async (req: MulterRequest, res: Response) => { const dailyUpdateData = await DailyUpdatesServices.createDailyUpdate( { constructionStage: req.body.constructionStage, + workCompleted: req.body.workCompleted || null, description: req.body.description || null, projectId: req.body.projectId || null, rawMaterials: rawMaterials, @@ -478,6 +479,10 @@ export const updateDailyUpdate = async (req: MulterRequest, res: Response) => { updateData.constructionStage = req.body.constructionStage; } + if (req.body.workCompleted !== undefined) { + updateData.workCompleted = req.body.workCompleted || null; + } + if (req.body.description !== undefined) { updateData.description = req.body.description || null; } diff --git a/src/modules/daily-updates/daily-updates.services.ts b/src/modules/daily-updates/daily-updates.services.ts index 8c24c68..4c35d28 100644 --- a/src/modules/daily-updates/daily-updates.services.ts +++ b/src/modules/daily-updates/daily-updates.services.ts @@ -17,6 +17,7 @@ export const createDailyUpdate = async ( data: { constructionStage: string; description?: string | null; + workCompleted?: string | null; projectId?: string | null; rawMaterials?: Array<{ materialName: string; @@ -30,7 +31,7 @@ export const createDailyUpdate = async ( supervisorId?: string ) => { // Validate construction stage - const validStages = ["Foundation", "Framing", "Plumbing & Electrical", "Interior Walls", "Painting", "Finishing"]; + const validStages = ["Foundation", "Framing", "Plumbing & Electrical", "Interior Walls", "Painting", "Finishing", "Others"]; if (!validStages.includes(data.constructionStage)) { throw new Error(`Invalid construction stage. Must be one of: ${validStages.join(", ")}`); } @@ -126,6 +127,7 @@ export const createDailyUpdate = async ( data: { constructionStage: stageEnum, description: data.description || null, + workCompleted: data.workCompleted || null, projectId: validProjectId, rawMaterials: data.rawMaterials ? JSON.stringify(data.rawMaterials) : "[]", // Store as JSON string if your DB expects it or rely on Prisma Json type status: statusEnum, @@ -504,6 +506,7 @@ export const updateDailyUpdate = async ( updateData: { constructionStage?: string; description?: string | null; + workCompleted?: string | null; projectId?: string | null; rawMaterials?: Array<{ materialName: string; @@ -529,7 +532,7 @@ export const updateDailyUpdate = async ( // Validate and update construction stage if provided if (updateData.constructionStage !== undefined) { - const validStages = ["Foundation", "Framing", "Plumbing & Electrical", "Interior Walls", "Painting", "Finishing"]; + const validStages = ["Foundation", "Framing", "Plumbing & Electrical", "Interior Walls", "Painting", "Finishing", "Others"]; if (!validStages.includes(updateData.constructionStage)) { throw new Error(`Invalid construction stage. Must be one of: ${validStages.join(", ")}`); } @@ -541,6 +544,11 @@ export const updateDailyUpdate = async ( dataToUpdate.constructionStage = stageEnum; } + // Update workCompleted if provided + if (updateData.workCompleted !== undefined) { + dataToUpdate.workCompleted = updateData.workCompleted || null; + } + // Validate and update status if provided if (updateData.status !== undefined) { const validStatuses = ["pending", "approved", "rejected"]; @@ -1476,13 +1484,15 @@ export const markStageComplete = async (projectId: string, stage: string, superv return ConstructionStage.Plumbing___Electrical; if (clean === "Interior_Walls" || clean === "Interior_walls") return ConstructionStage.Interior_Walls; + if (clean === "Others") + return ConstructionStage.Others; // Literal match check const validValues = Object.values(ConstructionStage); if (validValues.includes(clean as ConstructionStage)) return clean as ConstructionStage; // Fallback or fuzzy match could go here, but let's be strict with a better error - throw new Error(`Invalid construction stage: "${s}". Valid stages are: Foundation, Framing, Plumbing & Electrical, Interior Walls, Painting, Finishing`); + throw new Error(`Invalid construction stage: "${s}". Valid stages are: Foundation, Framing, Plumbing & Electrical, Interior Walls, Painting, Finishing, Others`); }; const stageEnum = normalizeInputStage(stage); @@ -1543,6 +1553,8 @@ export const approveStage = async (projectId: string, stage: string, userId: str return ConstructionStage.Plumbing___Electrical; if (clean === "Interior_Walls" || clean === "Interior_walls") return ConstructionStage.Interior_Walls; + if (clean === "Others") + return ConstructionStage.Others; const validValues = Object.values(ConstructionStage); if (validValues.includes(clean as ConstructionStage)) return clean as ConstructionStage; diff --git a/test_run.js b/test_run.js new file mode 100644 index 0000000..6f6e384 --- /dev/null +++ b/test_run.js @@ -0,0 +1,20 @@ +const { PrismaClient } = require('@prisma/client'); +const prisma = new PrismaClient(); + +async function main() { + try { + await prisma.dailyUpdate.findMany(); + console.log('daily ok'); + } catch(e) { + console.error('Daily update error:', e.message); + } + + try { + await prisma.project.findMany(); + console.log('project ok'); + } catch(e) { + console.error('Project error:', e.message); + } +} + +main().finally(() => prisma.$disconnect()); diff --git a/verify_fix.ts b/verify_fix.ts new file mode 100644 index 0000000..35faccc --- /dev/null +++ b/verify_fix.ts @@ -0,0 +1,22 @@ +import prisma from './src/config/prisma.client'; + +async function verify() { + try { + const dailyUpdates = await prisma.dailyUpdate.findMany({ take: 1 }); + console.log('Daily Update Fetch OK'); + } catch (e: any) { + console.error('Daily Update Fetch Failed:', e.message); + } + + try { + const projects = await prisma.project.findMany({ + take: 1, + include: { dailyUpdates: true } + }); + console.log('Project Fetch OK'); + } catch (e: any) { + console.error('Project Fetch Failed:', e.message); + } +} + +verify().finally(() => prisma.$disconnect());