nuevas correciones al formulario y esquema base de datos para osp
This commit is contained in:
@@ -108,10 +108,19 @@ export class MinioService implements OnModuleInit {
|
||||
|
||||
async delete(objectName: string): Promise<void> {
|
||||
try {
|
||||
await this.minioClient.removeObject(this.bucketName, objectName);
|
||||
this.logger.log(`Object "${objectName}" deleted successfully.`);
|
||||
// Ensure we don't have a leading slash which can cause issues with removeObject
|
||||
const cleanedName = objectName.startsWith('/')
|
||||
? objectName.slice(1)
|
||||
: objectName;
|
||||
|
||||
await this.minioClient.removeObject(this.bucketName, cleanedName);
|
||||
this.logger.log(
|
||||
`Object "${cleanedName}" deleted successfully from bucket "${this.bucketName}".`,
|
||||
);
|
||||
} catch (error: any) {
|
||||
this.logger.error(`Error deleting file: ${error.message}`);
|
||||
this.logger.error(
|
||||
`Error deleting file "${objectName}": ${error.message}`,
|
||||
);
|
||||
// We don't necessarily want to throw if the file is already gone
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
ALTER TABLE "training_surveys" ADD COLUMN "internal_distribution_zone" text;--> statement-breakpoint
|
||||
ALTER TABLE "training_surveys" ADD COLUMN "is_exporting" boolean DEFAULT false NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE "training_surveys" ADD COLUMN "external_country" text;--> statement-breakpoint
|
||||
ALTER TABLE "training_surveys" ADD COLUMN "external_city" text;--> statement-breakpoint
|
||||
ALTER TABLE "training_surveys" ADD COLUMN "external_description" text;--> statement-breakpoint
|
||||
ALTER TABLE "training_surveys" ADD COLUMN "external_quantity" text;--> statement-breakpoint
|
||||
ALTER TABLE "training_surveys" ADD COLUMN "external_unit" text;--> statement-breakpoint
|
||||
ALTER TABLE "training_surveys" ADD COLUMN "women_count" integer DEFAULT 0 NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE "training_surveys" ADD COLUMN "men_count" integer DEFAULT 0 NOT NULL;
|
||||
@@ -0,0 +1,5 @@
|
||||
DROP INDEX "training_surveys_index_00";--> statement-breakpoint
|
||||
ALTER TABLE "training_surveys" ADD COLUMN "coor_full_name" text NOT NULL;--> statement-breakpoint
|
||||
CREATE INDEX "training_surveys_index_00" ON "training_surveys" USING btree ("coor_full_name");--> statement-breakpoint
|
||||
ALTER TABLE "training_surveys" DROP COLUMN "firstname";--> statement-breakpoint
|
||||
ALTER TABLE "training_surveys" DROP COLUMN "lastname";
|
||||
2098
apps/api/src/database/migrations/meta/0022_snapshot.json
Normal file
2098
apps/api/src/database/migrations/meta/0022_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
2092
apps/api/src/database/migrations/meta/0023_snapshot.json
Normal file
2092
apps/api/src/database/migrations/meta/0023_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -155,6 +155,20 @@
|
||||
"when": 1771901546945,
|
||||
"tag": "0021_warm_machine_man",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 22,
|
||||
"version": "7",
|
||||
"when": 1772031518006,
|
||||
"tag": "0022_nervous_dragon_lord",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 23,
|
||||
"version": "7",
|
||||
"when": 1772032122473,
|
||||
"tag": "0023_sticky_slayback",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -48,8 +48,7 @@ export const trainingSurveys = t.pgTable(
|
||||
{
|
||||
// === 1. IDENTIFICADORES Y DATOS DE VISITA ===
|
||||
id: t.serial('id').primaryKey(),
|
||||
firstname: t.text('firstname').notNull(),
|
||||
lastname: t.text('lastname').notNull(),
|
||||
coorFullName: t.text('coor_full_name').notNull(),
|
||||
visitDate: t.timestamp('visit_date').notNull(),
|
||||
coorPhone: t.text('coor_phone'),
|
||||
|
||||
@@ -133,6 +132,20 @@ export const trainingSurveys = t.pgTable(
|
||||
familyBurden: t.integer('family_burden'),
|
||||
numberOfChildren: t.integer('number_of_children'),
|
||||
generalObservations: t.text('general_observations'),
|
||||
|
||||
// === 4. DATOS DE DISTRIBUCIÓN Y EXPORTACIÓN ===
|
||||
internalDistributionZone: t.text('internal_distribution_zone'),
|
||||
isExporting: t.boolean('is_exporting').notNull().default(false),
|
||||
externalCountry: t.text('external_country'),
|
||||
externalCity: t.text('external_city'),
|
||||
externalDescription: t.text('external_description'),
|
||||
externalQuantity: t.text('external_quantity'),
|
||||
externalUnit: t.text('external_unit'),
|
||||
|
||||
// === 5. MANO DE OBRA ===
|
||||
womenCount: t.integer('women_count').notNull().default(0),
|
||||
menCount: t.integer('men_count').notNull().default(0),
|
||||
|
||||
// Fotos
|
||||
photo1: t.text('photo1'),
|
||||
photo2: t.text('photo2'),
|
||||
@@ -149,7 +162,7 @@ export const trainingSurveys = t.pgTable(
|
||||
(trainingSurveys) => ({
|
||||
trainingSurveysIndex: t
|
||||
.index('training_surveys_index_00')
|
||||
.on(trainingSurveys.firstname),
|
||||
.on(trainingSurveys.coorFullName),
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDateString,
|
||||
IsEmail,
|
||||
IsInt,
|
||||
@@ -15,11 +14,7 @@ export class CreateTrainingDto {
|
||||
// === 1. DATOS BÁSICOS ===
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
firstname: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
lastname: string;
|
||||
coorFullName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsDateString()
|
||||
@@ -77,16 +72,14 @@ export class CreateTrainingDto {
|
||||
structureType?: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsBoolean()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value === 'true' || value === true) // Convierte "false" -> false
|
||||
hasTransport?: boolean;
|
||||
hasTransport?: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsBoolean()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value === 'true' || value === true)
|
||||
isOpenSpace?: boolean;
|
||||
isOpenSpace?: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@@ -209,7 +202,56 @@ export class CreateTrainingDto {
|
||||
@IsEmail()
|
||||
communalCouncilEmail?: string;
|
||||
|
||||
// === 6. LISTAS (Arrays JSON) ===
|
||||
// === 6. DISTRIBUCIÓN Y EXPORTACIÓN ===
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
internalDistributionZone?: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
isExporting?: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
externalCountry?: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
externalCity?: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
externalDescription?: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
externalQuantity?: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
externalUnit?: string;
|
||||
|
||||
// === 7. MANO DE OBRA ===
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
womenCount?: number;
|
||||
|
||||
@ApiProperty()
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
menCount?: number;
|
||||
|
||||
// === 8. LISTAS (Arrays JSON) ===
|
||||
// Reciben un string JSON '[{...}]' y lo convierten a Objeto JS real
|
||||
|
||||
@ApiProperty()
|
||||
|
||||
@@ -107,17 +107,7 @@ export class TrainingService {
|
||||
// 2. Total Productores (Columna plana que mantuviste)
|
||||
this.drizzle
|
||||
.select({
|
||||
sum: sql<number>`
|
||||
SUM(
|
||||
(
|
||||
SELECT SUM(
|
||||
COALESCE((item->>'menCount')::int, 0) +
|
||||
COALESCE((item->>'womenCount')::int, 0)
|
||||
)
|
||||
FROM jsonb_array_elements(${trainingSurveys.productList}) as item
|
||||
)
|
||||
)
|
||||
`,
|
||||
sum: sql<number>`SUM(${trainingSurveys.womenCount} + ${trainingSurveys.menCount})`,
|
||||
})
|
||||
.from(trainingSurveys)
|
||||
.where(whereCondition),
|
||||
@@ -244,20 +234,25 @@ export class TrainingService {
|
||||
private async deleteFile(fileUrl: string) {
|
||||
if (!fileUrl) return;
|
||||
|
||||
// Extract object name from URL
|
||||
// URL format: http://endpoint:port/bucket/folder/filename
|
||||
// Or it could be just the path if we decided that.
|
||||
// Assuming fileUrl is the full public URL from getPublicUrl
|
||||
try {
|
||||
const url = new URL(fileUrl);
|
||||
const pathname = url.pathname; // /bucket/folder/filename
|
||||
const parts = pathname.split('/');
|
||||
// parts[0] is '', parts[1] is bucket, parts[2..] is objectName
|
||||
const objectName = parts.slice(2).join('/');
|
||||
// If it's a full URL, we need to extract the part after the bucket name
|
||||
if (fileUrl.startsWith('http')) {
|
||||
const url = new URL(fileUrl);
|
||||
const pathname = url.pathname; // /bucket/folder/filename
|
||||
const parts = pathname.split('/').filter(Boolean); // ['bucket', 'folder', 'filename']
|
||||
|
||||
await this.minioService.delete(objectName);
|
||||
// The first part is the bucket name, the rest is the object name
|
||||
if (parts.length >= 2) {
|
||||
const objectName = parts.slice(1).join('/');
|
||||
await this.minioService.delete(objectName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If it's not a URL or doesn't match the expected format, pass it as is
|
||||
await this.minioService.delete(fileUrl);
|
||||
} catch (error) {
|
||||
// If it's not a valid URL, maybe it's just the object name stored from before
|
||||
// Fallback if URL parsing fails
|
||||
await this.minioService.delete(fileUrl);
|
||||
}
|
||||
}
|
||||
@@ -292,6 +287,9 @@ export class TrainingService {
|
||||
state: Number(state) ?? null,
|
||||
municipality: Number(municipality) ?? null,
|
||||
parish: Number(parish) ?? null,
|
||||
hasTransport: rest.hasTransport === 'true' ? true : false,
|
||||
isOpenSpace: rest.isOpenSpace === 'true' ? true : false,
|
||||
isExporting: rest.isExporting === 'true' ? true : false,
|
||||
createdBy: userId,
|
||||
updatedBy: userId,
|
||||
})
|
||||
@@ -359,6 +357,12 @@ export class TrainingService {
|
||||
|
||||
// actualizamos el id del usuario que actualizo el registro
|
||||
updateData.updatedBy = userId;
|
||||
updateData.hasTransport =
|
||||
updateTrainingDto.hasTransport === 'true' ? true : false;
|
||||
updateData.isOpenSpace =
|
||||
updateTrainingDto.isOpenSpace === 'true' ? true : false;
|
||||
updateData.isExporting =
|
||||
updateTrainingDto.isExporting === 'true' ? true : false;
|
||||
|
||||
const [updatedRecord] = await this.drizzle
|
||||
.update(trainingSurveys)
|
||||
@@ -402,8 +406,7 @@ export class TrainingService {
|
||||
|
||||
// const records = await this.drizzle
|
||||
// .select({
|
||||
// firstname: trainingSurveys.firstname,
|
||||
// lastname: trainingSurveys.lastname,
|
||||
// coorFullName: trainingSurveys.coorFullName,
|
||||
// visitDate: trainingSurveys.visitDate,
|
||||
// stateName: states.name,
|
||||
// municipalityName: municipalities.name,
|
||||
@@ -451,8 +454,7 @@ export class TrainingService {
|
||||
// const dateStr = date.toLocaleDateString('es-VE');
|
||||
// const timeStr = date.toLocaleTimeString('es-VE');
|
||||
|
||||
// sheet.cell(`A${currentRow}`).value(record.firstname);
|
||||
// sheet.cell(`B${currentRow}`).value(record.lastname);
|
||||
// sheet.cell(`A${currentRow}`).value(record.coorFullName);
|
||||
// sheet.cell(`C${currentRow}`).value(dateStr);
|
||||
// sheet.cell(`D${currentRow}`).value(timeStr);
|
||||
// sheet.cell(`E${currentRow}`).value(record.stateName || '');
|
||||
|
||||
Reference in New Issue
Block a user