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:)) pos.group(":id") { $0.get(use: detail(req:)) } } @Sendable func index(req: Request) async throws -> View { let params = try? req.query.decode(PurchaseOrderIndex.self) let purchaseOrdersPage = try await api2.fetchPage(params?.page ?? 1, limit: params?.limit ?? 50, 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( page: purchaseOrdersPage, form: .create(branches: branches, employees: employees) ) ) } @Sendable func detail(req: Request) async throws -> View { guard let id = req.parameters.get("id", as: PurchaseOrder.IDValue.self) else { throw Abort(.badRequest, reason: "Id not supplied.") } let purchaseOrder = try await api2.get(id: id, on: req.db) return try await req.view.render("purchaseOrders/detail", ["purchaseOrder": purchaseOrder]) } @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 PurchaseOrderIndex: Content { let page: Int? let limit: Int? } private struct PurchaseOrderCTX: Content { let purchaseOrders: [PurchaseOrder.DTO] let page: Int let limit: Int let hasNext: Bool let hasPrevious: Bool let form: PurchaseOrderFormCTX? init(page: Page, form: PurchaseOrderFormCTX?) { self.purchaseOrders = page.items self.page = page.metadata.page self.limit = page.metadata.per self.hasNext = page.metadata.hasNext self.hasPrevious = page.metadata.page > 1 self.form = form } } private extension PageMetadata { var hasNext: Bool { total > (page * per) } } 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) } }