import Fluent import Vapor struct PurchaseOrderViewController: RouteCollection { private let employeesApi = EmployeeApiController() private let branches = VendorBranchDB() private let api = ApiController() private let api2 = PurchaseOrderDB() func boot(routes: any RoutesBuilder) throws { let pos = routes.protected.grouped("purchase-orders") pos.get(use: index(req:)) pos.post(use: create(req:)) } // TODO: Use pageinated version. @Sendable func index(req: Request) async throws -> View { let purchaseOrders = try await api2.fetchAll(on: req.db) let branches = try await self.branches.getBranches(req: req) let employees = try await employeesApi.index(req: req) req.logger.debug("Branches: \(branches)") return try await req.view.render( "purchaseOrders/index", PurchaseOrderCTX( purchaseOrders: purchaseOrders, form: .create(branches: branches, employees: employees) ) ) } @Sendable func create(req: Request) async throws -> View { try PurchaseOrder.FormCreate.validate(content: req) let createdById = try req.auth.require(User.self).requireID() let create = try req.content.decode(PurchaseOrder.FormCreate.self).toCreate() let purchaseOrder = try await api2.create(create, createdById: createdById, on: req.db) return try await req.view.render("purchaseOrders/table-row", purchaseOrder) } } private struct PurchaseOrderCTX: Content { let purchaseOrders: [PurchaseOrder.DTO] let form: PurchaseOrderFormCTX? } private struct PurchaseOrderFormCTX: Content { let htmxForm: HtmxFormCTX struct Context: Content { let branches: [VendorBranch.FormDTO] let employees: [Employee.DTO] } static func create(branches: [VendorBranch.FormDTO], employees: [Employee.DTO]) -> Self { .init(htmxForm: .init( formClass: "po-form", formId: "po-form", htmxTargetUrl: .post("/purchase-orders"), htmxTarget: "#po-table-body", htmxPushUrl: false, htmxResetAfterRequest: true, htmxSwapOob: nil, htmxSwap: .afterbegin, context: .init(branches: branches, employees: employees) )) } } extension VendorBranch { struct FormDTO: Content { let id: UUID let name: String let vendor: Vendor.DTO } func toFormDTO() throws -> VendorBranch.FormDTO { try .init( id: requireID(), name: name, vendor: vendor.toDTO() ) } } private extension PurchaseOrder { struct FormCreate: Content { let id: Int? let workOrder: String? let materials: String let customer: String let truckStock: Bool? let createdForID: Employee.IDValue let vendorBranchID: VendorBranch.IDValue // TODO: Remove. func toModel(createdByID: User.IDValue) -> PurchaseOrder { .init( id: id, workOrder: workOrder != nil ? (workOrder == "" ? nil : Int(workOrder!)) : nil, materials: materials, customer: customer, truckStock: truckStock ?? false, createdByID: createdByID, createdForID: createdForID, vendorBranchID: vendorBranchID, createdAt: nil, updatedAt: nil ) } func toCreate() -> PurchaseOrder.Create { .init( id: id, workOrder: workOrder != nil ? (workOrder == "" ? nil : Int(workOrder!)) : nil, materials: materials, customer: customer, truckStock: truckStock, createdForID: createdForID, vendorBranchID: vendorBranchID ) } } } private extension VendorBranchDB { func getBranches(req: Request) async throws -> [VendorBranch.FormDTO] { try await VendorBranch.query(on: req.db) .with(\.$vendor) .all() .map { try $0.toFormDTO() } } } extension PurchaseOrder.FormCreate: Validatable { static func validations(_ validations: inout Validations) { validations.add("materials", as: String.self, is: !.empty) validations.add("customer", as: String.self, is: !.empty) } }