feat: agregar endpoints atómicos, auth de descuentos y limpiar deuda técnica
This commit is contained in:
1 parent
2d6c29a1f3
commit
1aa64e4bd0
50 files changed
+726
-303
No files matched your search
@@ -1,4 +1,12 @@
|
||||
import { Body, Controller, Get, Inject, Param, ParseIntPipe, Post } from "@nestjs/common";
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Post,
|
||||
} from "@nestjs/common";
|
||||
import type { CreateCashMovementInput } from "../../dtos/cash-movement.dto.js";
|
||||
import { CASH_MOVEMENT_SERVICE } from "./interfaces/cash-movement-service.interface.js";
|
||||
import type { ICashMovementService } from "./interfaces/cash-movement-service.interface.js";
|
||||
@@ -6,7 +14,8 @@ import type { ICashMovementService } from "./interfaces/cash-movement-service.in
|
||||
@Controller("cash-movements")
|
||||
export class CashMovementController {
|
||||
constructor(
|
||||
@Inject(CASH_MOVEMENT_SERVICE) private readonly cashMovementService: ICashMovementService,
|
||||
@Inject(CASH_MOVEMENT_SERVICE)
|
||||
private readonly cashMovementService: ICashMovementService,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
import { Inject, Injectable } from "@nestjs/common";
|
||||
import { DB } from "../../database/database.tokens.js";
|
||||
import type { Database } from "../../prisma/db.js";
|
||||
import type { CashMovement, CashMovementKey } from "../../entities/cash-movement.entity.js";
|
||||
import type {
|
||||
CashMovement,
|
||||
CashMovementKey,
|
||||
} from "../../entities/cash-movement.entity.js";
|
||||
import type { CreateCashMovementInput } from "../../dtos/cash-movement.dto.js";
|
||||
import type { ICashMovementRepository } from "./interfaces/cash-movement-repository.interface.js";
|
||||
|
||||
const COLUMNS = ["id", "sessionId", "type", "amount", "reason", "userId", "createdAt"] as const;
|
||||
const COLUMNS = [
|
||||
"id",
|
||||
"sessionId",
|
||||
"type",
|
||||
"amount",
|
||||
"reason",
|
||||
"userId",
|
||||
"createdAt",
|
||||
] as const;
|
||||
|
||||
@Injectable()
|
||||
export class CashMovementRepository implements ICashMovementRepository {
|
||||
@@ -16,11 +27,17 @@ export class CashMovementRepository implements ICashMovementRepository {
|
||||
}
|
||||
|
||||
findAll(): Promise<CashMovement[]> {
|
||||
return this.model.select(...COLUMNS).all().toArray();
|
||||
return this.model
|
||||
.select(...COLUMNS)
|
||||
.all()
|
||||
.toArray();
|
||||
}
|
||||
|
||||
findById(where: CashMovementKey): Promise<CashMovement | null> {
|
||||
return this.model.select(...COLUMNS).where(where).first();
|
||||
return this.model
|
||||
.select(...COLUMNS)
|
||||
.where(where)
|
||||
.first();
|
||||
}
|
||||
|
||||
create(data: CreateCashMovementInput): Promise<CashMovement> {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Inject, Injectable, NotFoundException } from "@nestjs/common";
|
||||
import type { CashMovement, CashMovementKey } from "../../entities/cash-movement.entity.js";
|
||||
import type {
|
||||
CashMovement,
|
||||
CashMovementKey,
|
||||
} from "../../entities/cash-movement.entity.js";
|
||||
import type { CreateCashMovementInput } from "../../dtos/cash-movement.dto.js";
|
||||
import {
|
||||
CASH_MOVEMENT_REPOSITORY,
|
||||
@@ -10,7 +13,8 @@ import type { ICashMovementService } from "./interfaces/cash-movement-service.in
|
||||
@Injectable()
|
||||
export class CashMovementService implements ICashMovementService {
|
||||
constructor(
|
||||
@Inject(CASH_MOVEMENT_REPOSITORY) private readonly repository: ICashMovementRepository,
|
||||
@Inject(CASH_MOVEMENT_REPOSITORY)
|
||||
private readonly repository: ICashMovementRepository,
|
||||
) {}
|
||||
|
||||
findAll(): Promise<CashMovement[]> {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import type { CashMovement, CashMovementKey } from "../../../entities/cash-movement.entity.js";
|
||||
import type {
|
||||
CashMovement,
|
||||
CashMovementKey,
|
||||
} from "../../../entities/cash-movement.entity.js";
|
||||
import type { CreateCashMovementInput } from "../../../dtos/cash-movement.dto.js";
|
||||
|
||||
export const CASH_MOVEMENT_REPOSITORY = Symbol("CASH_MOVEMENT_REPOSITORY");
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import type { CashMovement, CashMovementKey } from "../../../entities/cash-movement.entity.js";
|
||||
import type {
|
||||
CashMovement,
|
||||
CashMovementKey,
|
||||
} from "../../../entities/cash-movement.entity.js";
|
||||
import type { CreateCashMovementInput } from "../../../dtos/cash-movement.dto.js";
|
||||
|
||||
export const CASH_MOVEMENT_SERVICE = Symbol("CASH_MOVEMENT_SERVICE");
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
import { Body, Controller, Delete, Get, Inject, Param, ParseIntPipe, Patch, Post } from "@nestjs/common";
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Patch,
|
||||
Post,
|
||||
} from "@nestjs/common";
|
||||
import type {
|
||||
CreateCashRegisterSessionInput,
|
||||
UpdateCashRegisterSessionInput,
|
||||
@@ -29,7 +39,10 @@ export class CashRegisterSessionController {
|
||||
}
|
||||
|
||||
@Patch(":id")
|
||||
update(@Param("id", ParseIntPipe) id: number, @Body() body: UpdateCashRegisterSessionInput) {
|
||||
update(
|
||||
@Param("id", ParseIntPipe) id: number,
|
||||
@Body() body: UpdateCashRegisterSessionInput,
|
||||
) {
|
||||
return this.sessionService.update({ id }, body);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,14 @@ import { CashRegisterSessionService } from "./cash-register-session.service.js";
|
||||
@Module({
|
||||
controllers: [CashRegisterSessionController],
|
||||
providers: [
|
||||
{ provide: CASH_REGISTER_SESSION_REPOSITORY, useClass: CashRegisterSessionRepository },
|
||||
{ provide: CASH_REGISTER_SESSION_SERVICE, useClass: CashRegisterSessionService },
|
||||
{
|
||||
provide: CASH_REGISTER_SESSION_REPOSITORY,
|
||||
useClass: CashRegisterSessionRepository,
|
||||
},
|
||||
{
|
||||
provide: CASH_REGISTER_SESSION_SERVICE,
|
||||
useClass: CashRegisterSessionService,
|
||||
},
|
||||
],
|
||||
exports: [CASH_REGISTER_SESSION_SERVICE],
|
||||
})
|
||||
|
||||
@@ -31,9 +31,13 @@ export class CashRegisterSessionService
|
||||
super(sessionRepository);
|
||||
}
|
||||
|
||||
override async create(data: CreateCashRegisterSessionInput): Promise<CashRegisterSession> {
|
||||
override async create(
|
||||
data: CreateCashRegisterSessionInput,
|
||||
): Promise<CashRegisterSession> {
|
||||
const all = await this.sessionRepository.findAll();
|
||||
const alreadyOpen = all.some((s) => s.inventoryId === data.inventoryId && s.status === "open");
|
||||
const alreadyOpen = all.some(
|
||||
(s) => s.inventoryId === data.inventoryId && s.status === "open",
|
||||
);
|
||||
if (alreadyOpen) {
|
||||
throw new BadRequestException("Ya hay una caja abierta en este almacén");
|
||||
}
|
||||
|
||||
+3
-1
@@ -8,7 +8,9 @@ import type {
|
||||
UpdateCashRegisterSessionInput,
|
||||
} from "../../../dtos/cash-register-session.dto.js";
|
||||
|
||||
export const CASH_REGISTER_SESSION_REPOSITORY = Symbol("CASH_REGISTER_SESSION_REPOSITORY");
|
||||
export const CASH_REGISTER_SESSION_REPOSITORY = Symbol(
|
||||
"CASH_REGISTER_SESSION_REPOSITORY",
|
||||
);
|
||||
|
||||
export type ICashRegisterSessionRepository = IRepository<
|
||||
CashRegisterSession,
|
||||
|
||||
+3
-1
@@ -8,7 +8,9 @@ import type {
|
||||
UpdateCashRegisterSessionInput,
|
||||
} from "../../../dtos/cash-register-session.dto.js";
|
||||
|
||||
export const CASH_REGISTER_SESSION_SERVICE = Symbol("CASH_REGISTER_SESSION_SERVICE");
|
||||
export const CASH_REGISTER_SESSION_SERVICE = Symbol(
|
||||
"CASH_REGISTER_SESSION_SERVICE",
|
||||
);
|
||||
|
||||
export type ICashRegisterSessionService = IService<
|
||||
CashRegisterSession,
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
import { Body, Controller, Delete, Get, Inject, Param, ParseIntPipe, Patch, Post } from "@nestjs/common";
|
||||
import type { CreateCustomerInput, UpdateCustomerInput } from "../../dtos/customer.dto.js";
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Patch,
|
||||
Post,
|
||||
} from "@nestjs/common";
|
||||
import type {
|
||||
CreateCustomerInput,
|
||||
UpdateCustomerInput,
|
||||
} from "../../dtos/customer.dto.js";
|
||||
import { CUSTOMER_SERVICE } from "./interfaces/customer-service.interface.js";
|
||||
import type { ICustomerService } from "./interfaces/customer-service.interface.js";
|
||||
|
||||
@Controller("customers")
|
||||
export class CustomerController {
|
||||
constructor(@Inject(CUSTOMER_SERVICE) private readonly customerService: ICustomerService) {}
|
||||
constructor(
|
||||
@Inject(CUSTOMER_SERVICE)
|
||||
private readonly customerService: ICustomerService,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
@@ -23,7 +39,10 @@ export class CustomerController {
|
||||
}
|
||||
|
||||
@Patch(":id")
|
||||
update(@Param("id", ParseIntPipe) id: number, @Body() body: UpdateCustomerInput) {
|
||||
update(
|
||||
@Param("id", ParseIntPipe) id: number,
|
||||
@Body() body: UpdateCustomerInput,
|
||||
) {
|
||||
return this.customerService.update({ id }, body);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { BaseRepository } from "../../common/base.repository.js";
|
||||
import type { CreateCustomerInput, UpdateCustomerInput } from "../../dtos/customer.dto.js";
|
||||
import type {
|
||||
CreateCustomerInput,
|
||||
UpdateCustomerInput,
|
||||
} from "../../dtos/customer.dto.js";
|
||||
import type { Customer, CustomerKey } from "../../entities/customer.entity.js";
|
||||
import type { ICustomerRepository } from "./interfaces/customer-repository.interface.js";
|
||||
|
||||
@Injectable()
|
||||
export class CustomerRepository
|
||||
extends BaseRepository<Customer, CreateCustomerInput, UpdateCustomerInput, CustomerKey>
|
||||
extends BaseRepository<
|
||||
Customer,
|
||||
CreateCustomerInput,
|
||||
UpdateCustomerInput,
|
||||
CustomerKey
|
||||
>
|
||||
implements ICustomerRepository
|
||||
{
|
||||
protected readonly columns = ["id", "name", "phone", "email", "taxId", "createdAt"] as const;
|
||||
protected readonly columns = [
|
||||
"id",
|
||||
"name",
|
||||
"phone",
|
||||
"email",
|
||||
"taxId",
|
||||
"createdAt",
|
||||
] as const;
|
||||
|
||||
protected get model() {
|
||||
return this.db.orm.public.Customer;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { Inject, Injectable } from "@nestjs/common";
|
||||
import { BaseService } from "../../common/base.service.js";
|
||||
import type { CreateCustomerInput, UpdateCustomerInput } from "../../dtos/customer.dto.js";
|
||||
import type {
|
||||
CreateCustomerInput,
|
||||
UpdateCustomerInput,
|
||||
} from "../../dtos/customer.dto.js";
|
||||
import type { Customer, CustomerKey } from "../../entities/customer.entity.js";
|
||||
import {
|
||||
CUSTOMER_REPOSITORY,
|
||||
@@ -10,7 +13,12 @@ import type { ICustomerService } from "./interfaces/customer-service.interface.j
|
||||
|
||||
@Injectable()
|
||||
export class CustomerService
|
||||
extends BaseService<Customer, CreateCustomerInput, UpdateCustomerInput, CustomerKey>
|
||||
extends BaseService<
|
||||
Customer,
|
||||
CreateCustomerInput,
|
||||
UpdateCustomerInput,
|
||||
CustomerKey
|
||||
>
|
||||
implements ICustomerService
|
||||
{
|
||||
constructor(@Inject(CUSTOMER_REPOSITORY) repository: ICustomerRepository) {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { IRepository } from "../../../common/repository.interface.js";
|
||||
import type { Customer, CustomerKey } from "../../../entities/customer.entity.js";
|
||||
import type { CreateCustomerInput, UpdateCustomerInput } from "../../../dtos/customer.dto.js";
|
||||
import type {
|
||||
Customer,
|
||||
CustomerKey,
|
||||
} from "../../../entities/customer.entity.js";
|
||||
import type {
|
||||
CreateCustomerInput,
|
||||
UpdateCustomerInput,
|
||||
} from "../../../dtos/customer.dto.js";
|
||||
|
||||
export const CUSTOMER_REPOSITORY = Symbol("CUSTOMER_REPOSITORY");
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { IService } from "../../../common/service.interface.js";
|
||||
import type { Customer, CustomerKey } from "../../../entities/customer.entity.js";
|
||||
import type { CreateCustomerInput, UpdateCustomerInput } from "../../../dtos/customer.dto.js";
|
||||
import type {
|
||||
Customer,
|
||||
CustomerKey,
|
||||
} from "../../../entities/customer.entity.js";
|
||||
import type {
|
||||
CreateCustomerInput,
|
||||
UpdateCustomerInput,
|
||||
} from "../../../dtos/customer.dto.js";
|
||||
|
||||
export const CUSTOMER_SERVICE = Symbol("CUSTOMER_SERVICE");
|
||||
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Body, Controller, Get, Inject, Param, ParseIntPipe, Post } from "@nestjs/common";
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Post,
|
||||
} from "@nestjs/common";
|
||||
import type { CreateInventoryMovementInput } from "../../dtos/inventory-movement.dto.js";
|
||||
import {
|
||||
INVENTORY_MOVEMENT_SERVICE,
|
||||
|
||||
@@ -12,7 +12,10 @@ import { InventoryMovementService } from "./inventory-movement.service.js";
|
||||
imports: [WarehouseModule, ProductModule, SupplierModule],
|
||||
controllers: [InventoryMovementController],
|
||||
providers: [
|
||||
{ provide: INVENTORY_MOVEMENT_REPOSITORY, useClass: InventoryMovementRepository },
|
||||
{
|
||||
provide: INVENTORY_MOVEMENT_REPOSITORY,
|
||||
useClass: InventoryMovementRepository,
|
||||
},
|
||||
{ provide: INVENTORY_MOVEMENT_SERVICE, useClass: InventoryMovementService },
|
||||
],
|
||||
exports: [INVENTORY_MOVEMENT_SERVICE],
|
||||
|
||||
@@ -20,9 +20,7 @@ const COLUMNS = [
|
||||
] as const;
|
||||
|
||||
@Injectable()
|
||||
export class InventoryMovementRepository
|
||||
implements IInventoryMovementRepository
|
||||
{
|
||||
export class InventoryMovementRepository implements IInventoryMovementRepository {
|
||||
constructor(@Inject(DB) private readonly db: Database) {}
|
||||
|
||||
private get model() {
|
||||
@@ -30,11 +28,17 @@ export class InventoryMovementRepository
|
||||
}
|
||||
|
||||
findAll(): Promise<InventoryMovement[]> {
|
||||
return this.model.select(...COLUMNS).all().toArray();
|
||||
return this.model
|
||||
.select(...COLUMNS)
|
||||
.all()
|
||||
.toArray();
|
||||
}
|
||||
|
||||
findById(where: InventoryMovementKey): Promise<InventoryMovement | null> {
|
||||
return this.model.select(...COLUMNS).where(where).first();
|
||||
return this.model
|
||||
.select(...COLUMNS)
|
||||
.where(where)
|
||||
.first();
|
||||
}
|
||||
|
||||
create(data: CreateInventoryMovementInput): Promise<InventoryMovement> {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { BadRequestException, Inject, Injectable, NotFoundException } from "@nestjs/common";
|
||||
import {
|
||||
BadRequestException,
|
||||
Inject,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from "@nestjs/common";
|
||||
import {
|
||||
PRODUCT_SERVICE,
|
||||
type IProductService,
|
||||
@@ -33,9 +38,11 @@ export class InventoryMovementService implements IInventoryMovementService {
|
||||
constructor(
|
||||
@Inject(INVENTORY_MOVEMENT_REPOSITORY)
|
||||
private readonly repository: IInventoryMovementRepository,
|
||||
@Inject(WAREHOUSE_SERVICE) private readonly warehouseService: IWarehouseService,
|
||||
@Inject(WAREHOUSE_SERVICE)
|
||||
private readonly warehouseService: IWarehouseService,
|
||||
@Inject(PRODUCT_SERVICE) private readonly productService: IProductService,
|
||||
@Inject(SUPPLIER_SERVICE) private readonly supplierService: ISupplierService,
|
||||
@Inject(SUPPLIER_SERVICE)
|
||||
private readonly supplierService: ISupplierService,
|
||||
) {}
|
||||
|
||||
findAll(): Promise<InventoryMovement[]> {
|
||||
@@ -66,17 +73,25 @@ export class InventoryMovementService implements IInventoryMovementService {
|
||||
if (data.type === "out") {
|
||||
const currentStock = existing?.stock ?? 0;
|
||||
if (currentStock < data.quantity) {
|
||||
throw new BadRequestException("Stock insuficiente para registrar la salida");
|
||||
throw new BadRequestException(
|
||||
"Stock insuficiente para registrar la salida",
|
||||
);
|
||||
}
|
||||
await this.warehouseService.update(key, { stock: currentStock - data.quantity });
|
||||
await this.warehouseService.update(key, {
|
||||
stock: currentStock - data.quantity,
|
||||
});
|
||||
} else {
|
||||
if (existing) {
|
||||
await this.warehouseService.update(key, { stock: existing.stock + data.quantity });
|
||||
await this.warehouseService.update(key, {
|
||||
stock: existing.stock + data.quantity,
|
||||
});
|
||||
} else {
|
||||
await this.warehouseService.create({ ...key, stock: data.quantity });
|
||||
}
|
||||
|
||||
const product = await this.productService.findById({ id: data.productId }).catch(() => null);
|
||||
const product = await this.productService
|
||||
.findById({ id: data.productId })
|
||||
.catch(() => null);
|
||||
if (product?.supplierId) {
|
||||
await this.supplierService.update(
|
||||
{ id: product.supplierId },
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { IRepository } from "../../../common/repository.interface.js";
|
||||
import type { OperatingDay, OperatingDayKey } from "../../../entities/operating-day.entity.js";
|
||||
import type { CreateOperatingDayInput, UpdateOperatingDayInput } from "../../../dtos/operating-day.dto.js";
|
||||
import type {
|
||||
OperatingDay,
|
||||
OperatingDayKey,
|
||||
} from "../../../entities/operating-day.entity.js";
|
||||
import type {
|
||||
CreateOperatingDayInput,
|
||||
UpdateOperatingDayInput,
|
||||
} from "../../../dtos/operating-day.dto.js";
|
||||
|
||||
export const OPERATING_DAY_REPOSITORY = Symbol("OPERATING_DAY_REPOSITORY");
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { IService } from "../../../common/service.interface.js";
|
||||
import type { OperatingDay, OperatingDayKey } from "../../../entities/operating-day.entity.js";
|
||||
import type { CreateOperatingDayInput, UpdateOperatingDayInput } from "../../../dtos/operating-day.dto.js";
|
||||
import type {
|
||||
OperatingDay,
|
||||
OperatingDayKey,
|
||||
} from "../../../entities/operating-day.entity.js";
|
||||
import type {
|
||||
CreateOperatingDayInput,
|
||||
UpdateOperatingDayInput,
|
||||
} from "../../../dtos/operating-day.dto.js";
|
||||
|
||||
export const OPERATING_DAY_SERVICE = Symbol("OPERATING_DAY_SERVICE");
|
||||
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
import { Body, Controller, Delete, Get, Inject, Param, ParseIntPipe, Patch, Post } from "@nestjs/common";
|
||||
import type { CreateOperatingDayInput, UpdateOperatingDayInput } from "../../dtos/operating-day.dto.js";
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Patch,
|
||||
Post,
|
||||
} from "@nestjs/common";
|
||||
import type {
|
||||
CreateOperatingDayInput,
|
||||
UpdateOperatingDayInput,
|
||||
} from "../../dtos/operating-day.dto.js";
|
||||
import { OPERATING_DAY_SERVICE } from "./interfaces/operating-day-service.interface.js";
|
||||
import type { IOperatingDayService } from "./interfaces/operating-day-service.interface.js";
|
||||
|
||||
@@ -26,7 +39,10 @@ export class OperatingDayController {
|
||||
}
|
||||
|
||||
@Patch(":id")
|
||||
update(@Param("id", ParseIntPipe) id: number, @Body() body: UpdateOperatingDayInput) {
|
||||
update(
|
||||
@Param("id", ParseIntPipe) id: number,
|
||||
@Body() body: UpdateOperatingDayInput,
|
||||
) {
|
||||
return this.operatingDayService.update({ id }, body);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,34 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { BaseRepository } from "../../common/base.repository.js";
|
||||
import type { CreateOperatingDayInput, UpdateOperatingDayInput } from "../../dtos/operating-day.dto.js";
|
||||
import type { OperatingDay, OperatingDayKey } from "../../entities/operating-day.entity.js";
|
||||
import type {
|
||||
CreateOperatingDayInput,
|
||||
UpdateOperatingDayInput,
|
||||
} from "../../dtos/operating-day.dto.js";
|
||||
import type {
|
||||
OperatingDay,
|
||||
OperatingDayKey,
|
||||
} from "../../entities/operating-day.entity.js";
|
||||
import type { IOperatingDayRepository } from "./interfaces/operating-day-repository.interface.js";
|
||||
|
||||
@Injectable()
|
||||
export class OperatingDayRepository
|
||||
extends BaseRepository<OperatingDay, CreateOperatingDayInput, UpdateOperatingDayInput, OperatingDayKey>
|
||||
extends BaseRepository<
|
||||
OperatingDay,
|
||||
CreateOperatingDayInput,
|
||||
UpdateOperatingDayInput,
|
||||
OperatingDayKey
|
||||
>
|
||||
implements IOperatingDayRepository
|
||||
{
|
||||
protected readonly columns = ["id", "date", "status", "openedById", "closedById", "openedAt", "closedAt"] as const;
|
||||
protected readonly columns = [
|
||||
"id",
|
||||
"date",
|
||||
"status",
|
||||
"openedById",
|
||||
"closedById",
|
||||
"openedAt",
|
||||
"closedAt",
|
||||
] as const;
|
||||
|
||||
protected get model() {
|
||||
return this.db.orm.public.OperatingDay;
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
import { BadRequestException, Inject, Injectable } from "@nestjs/common";
|
||||
import { BaseService } from "../../common/base.service.js";
|
||||
import type { CreateOperatingDayInput, UpdateOperatingDayInput } from "../../dtos/operating-day.dto.js";
|
||||
import type { OperatingDay, OperatingDayKey } from "../../entities/operating-day.entity.js";
|
||||
import type {
|
||||
CreateOperatingDayInput,
|
||||
UpdateOperatingDayInput,
|
||||
} from "../../dtos/operating-day.dto.js";
|
||||
import type {
|
||||
OperatingDay,
|
||||
OperatingDayKey,
|
||||
} from "../../entities/operating-day.entity.js";
|
||||
import {
|
||||
CASH_REGISTER_SESSION_SERVICE,
|
||||
type ICashRegisterSessionService,
|
||||
} from "../cash-register-session/interfaces/cash-register-session-service.interface.js";
|
||||
import { OPERATING_DAY_REPOSITORY, type IOperatingDayRepository } from "./interfaces/operating-day-repository.interface.js";
|
||||
import {
|
||||
OPERATING_DAY_REPOSITORY,
|
||||
type IOperatingDayRepository,
|
||||
} from "./interfaces/operating-day-repository.interface.js";
|
||||
import type { IOperatingDayService } from "./interfaces/operating-day-service.interface.js";
|
||||
|
||||
@Injectable()
|
||||
export class OperatingDayService
|
||||
extends BaseService<OperatingDay, CreateOperatingDayInput, UpdateOperatingDayInput, OperatingDayKey>
|
||||
extends BaseService<
|
||||
OperatingDay,
|
||||
CreateOperatingDayInput,
|
||||
UpdateOperatingDayInput,
|
||||
OperatingDayKey
|
||||
>
|
||||
implements IOperatingDayService
|
||||
{
|
||||
constructor(
|
||||
@@ -32,20 +46,29 @@ export class OperatingDayService
|
||||
return this.operatingDayRepository.create(data);
|
||||
}
|
||||
|
||||
override async update(where: OperatingDayKey, data: UpdateOperatingDayInput): Promise<OperatingDay> {
|
||||
override async update(
|
||||
where: OperatingDayKey,
|
||||
data: UpdateOperatingDayInput,
|
||||
): Promise<OperatingDay> {
|
||||
const current = await this.findById(where);
|
||||
|
||||
if (data.status === "closed") {
|
||||
if (current.status === "closed") {
|
||||
throw new BadRequestException("Esta fecha de operación ya está cerrada");
|
||||
throw new BadRequestException(
|
||||
"Esta fecha de operación ya está cerrada",
|
||||
);
|
||||
}
|
||||
const sessions = await this.sessionService.findAll();
|
||||
const hasOpenSession = sessions.some((s) => s.status === "open");
|
||||
if (hasOpenSession) {
|
||||
throw new BadRequestException("No se puede cerrar el día de operación con cajas abiertas");
|
||||
throw new BadRequestException(
|
||||
"No se puede cerrar el día de operación con cajas abiertas",
|
||||
);
|
||||
}
|
||||
if (!data.closedById) {
|
||||
throw new BadRequestException("Falta indicar quién cierra la fecha de operación");
|
||||
throw new BadRequestException(
|
||||
"Falta indicar quién cierra la fecha de operación",
|
||||
);
|
||||
}
|
||||
|
||||
const closed = await this.operatingDayRepository.update(where, {
|
||||
|
||||
@@ -7,9 +7,26 @@ import type {
|
||||
|
||||
export const PRODUCT_REPOSITORY = Symbol("PRODUCT_REPOSITORY");
|
||||
|
||||
export type IProductRepository = IRepository<
|
||||
export interface SetPriceInput {
|
||||
unitPrice: string;
|
||||
costUnit?: string;
|
||||
}
|
||||
|
||||
export interface IProductRepository extends IRepository<
|
||||
Product,
|
||||
CreateProductInput,
|
||||
UpdateProductInput,
|
||||
ProductKey
|
||||
>;
|
||||
> {
|
||||
setPrice(
|
||||
productId: number,
|
||||
data: SetPriceInput,
|
||||
): Promise<{
|
||||
id: number;
|
||||
productId: number;
|
||||
unitPrice: string;
|
||||
costUnit: string;
|
||||
status: string;
|
||||
}>;
|
||||
setCategories(productId: number, categoryIds: number[]): Promise<void>;
|
||||
}
|
||||
@@ -5,11 +5,26 @@ import type {
|
||||
UpdateProductInput,
|
||||
} from "../../../dtos/product.dto.js";
|
||||
|
||||
import type { SetPriceInput } from "./product-repository.interface.js";
|
||||
export type { SetPriceInput };
|
||||
|
||||
export const PRODUCT_SERVICE = Symbol("PRODUCT_SERVICE");
|
||||
|
||||
export type IProductService = IService<
|
||||
export interface IProductService extends IService<
|
||||
Product,
|
||||
CreateProductInput,
|
||||
UpdateProductInput,
|
||||
ProductKey
|
||||
>;
|
||||
> {
|
||||
setPrice(
|
||||
productId: number,
|
||||
data: SetPriceInput,
|
||||
): Promise<{
|
||||
id: number;
|
||||
productId: number;
|
||||
unitPrice: string;
|
||||
costUnit: string;
|
||||
status: string;
|
||||
}>;
|
||||
setCategories(productId: number, categoryIds: number[]): Promise<void>;
|
||||
}
|
||||
@@ -37,6 +37,22 @@ export class ProductController {
|
||||
return this.productService.create(body);
|
||||
}
|
||||
|
||||
@Post(":id/price")
|
||||
setPrice(
|
||||
@Param("id", ParseIntPipe) id: number,
|
||||
@Body() body: { unitPrice: string; costUnit?: string },
|
||||
) {
|
||||
return this.productService.setPrice(id, body);
|
||||
}
|
||||
|
||||
@Patch(":id/categories")
|
||||
syncCategoriesPatch(
|
||||
@Param("id", ParseIntPipe) id: number,
|
||||
@Body() body: { categoryIds: number[] },
|
||||
) {
|
||||
return this.productService.setCategories(id, body.categoryIds ?? []);
|
||||
}
|
||||
|
||||
@Patch(":id")
|
||||
update(
|
||||
@Param("id", ParseIntPipe) id: number,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return */
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { BaseRepository } from "../../common/base.repository.js";
|
||||
import type {
|
||||
@@ -32,4 +33,36 @@ export class ProductRepository
|
||||
protected get model() {
|
||||
return this.db.orm.public.Product;
|
||||
}
|
||||
|
||||
async setPrice(
|
||||
productId: number,
|
||||
data: { unitPrice: string; costUnit?: string },
|
||||
) {
|
||||
// 1. Archivar los precios activos anteriores para este producto
|
||||
await (this.db.orm.public.ProductHistory as any)
|
||||
.where({ productId, status: "active" })
|
||||
.update({ status: "archived" });
|
||||
|
||||
// 2. Crear el nuevo precio con status active
|
||||
const newHistory = await (this.db.orm.public.ProductHistory as any).create({
|
||||
productId,
|
||||
unitPrice: data.unitPrice,
|
||||
costUnit: data.costUnit ?? "0",
|
||||
status: "active",
|
||||
});
|
||||
|
||||
return newHistory;
|
||||
}
|
||||
|
||||
async setCategories(productId: number, categoryIds: number[]): Promise<void> {
|
||||
const categoryModel = this.db.orm.public.ProductCategory as any;
|
||||
// 1. Eliminar las categorías actuales del producto
|
||||
await categoryModel.where({ productId }).delete();
|
||||
|
||||
// 2. Insertar las nuevas categorías
|
||||
const uniqueIds = Array.from(new Set(categoryIds));
|
||||
for (const categoryId of uniqueIds) {
|
||||
await categoryModel.create({ productId, categoryId });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,22 @@ export class ProductService
|
||||
super(repository);
|
||||
}
|
||||
|
||||
async setPrice(
|
||||
productId: number,
|
||||
data: { unitPrice: string; costUnit?: string },
|
||||
) {
|
||||
await this.findById({ id: productId });
|
||||
return (this.repository as IProductRepository).setPrice(productId, data);
|
||||
}
|
||||
|
||||
async setCategories(productId: number, categoryIds: number[]): Promise<void> {
|
||||
await this.findById({ id: productId });
|
||||
return (this.repository as IProductRepository).setCategories(
|
||||
productId,
|
||||
categoryIds,
|
||||
);
|
||||
}
|
||||
|
||||
protected override notFoundMessage(where: ProductKey): string {
|
||||
return `Product ${where.id} not found`;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Inject, Injectable } from "@nestjs/common";
|
||||
import { BadRequestException, Inject, Injectable } from "@nestjs/common";
|
||||
import { BaseService } from "../../common/base.service.js";
|
||||
import type {
|
||||
CreateShopCartInput,
|
||||
@@ -25,6 +25,34 @@ export class ShopCartService
|
||||
super(repository);
|
||||
}
|
||||
|
||||
private validateDiscount(totalPriceStr?: string, discountStr?: string) {
|
||||
if (discountStr !== undefined) {
|
||||
const discount = Number(discountStr);
|
||||
if (Number.isNaN(discount) || discount < 0) {
|
||||
throw new BadRequestException("El descuento no puede ser negativo");
|
||||
}
|
||||
if (totalPriceStr !== undefined) {
|
||||
const total = Number(totalPriceStr);
|
||||
if (Number.isNaN(total) || total < 0) {
|
||||
throw new BadRequestException("El total no puede ser negativo");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override create(data: CreateShopCartInput): Promise<ShopCart> {
|
||||
this.validateDiscount(data.totalPrice, data.discount);
|
||||
return super.create(data);
|
||||
}
|
||||
|
||||
override update(
|
||||
where: ShopCartKey,
|
||||
data: UpdateShopCartInput,
|
||||
): Promise<ShopCart> {
|
||||
this.validateDiscount(data.totalPrice, data.discount);
|
||||
return super.update(where, data);
|
||||
}
|
||||
|
||||
protected override notFoundMessage(where: ShopCartKey): string {
|
||||
return `ShopCart ${where.id} not found`;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user