module util.validation.transaction; import handy_http_primitives; import std.array; import std.datetime; import util.validation.common; import transaction.dto; import transaction.data; import account.data; // class AtLeastOneLinkedAccountRule : ValidationRule!AddTransactionPayload { // Optional!ValidationError validate(in AddTransactionPayload payload) { // if (!payload.creditedAccountId && !payload.debitedAccountId) { // return ValidationError("creditedAccountId", "At least one account must be linked.").toOptional(); // } // return Optional!ValidationError.empty(); // } // } void validateTransactionPayload( TransactionVendorRepository vendorRepo, TransactionCategoryRepository categoryRepo, AccountRepository accountRepo, in AddTransactionPayload payload ) { if (!payload.creditedAccountId && !payload.debitedAccountId) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "At least one account must be linked."); } if ( payload.creditedAccountId && payload.debitedAccountId && payload.creditedAccountId.value == payload.debitedAccountId.value ) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Cannot link the same account as both credit and debit."); } if (payload.amount == 0) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Amount should be greater than 0."); } SysTime now = Clock.currTime(UTC()); SysTime timestamp = validateTimestampFormat(payload.timestamp); if (timestamp > now) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Cannot create transaction in the future."); } if (payload.vendorId && !vendorRepo.existsById(payload.vendorId.value)) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Vendor doesn't exist."); } if (payload.categoryId && !categoryRepo.existsById(payload.categoryId.value)) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Category doesn't exist."); } if (payload.creditedAccountId && !accountRepo.existsById(payload.creditedAccountId.value)) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Credited account doesn't exist."); } if (payload.debitedAccountId && !accountRepo.existsById(payload.debitedAccountId.value)) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Debited account doesn't exist."); } validateTags(payload.tags); if (payload.lineItems.length > 0) { long lineItemsTotal = 0; foreach (lineItem; payload.lineItems) { if (lineItem.categoryId && !categoryRepo.existsById(lineItem.categoryId.value)) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Line item's category doesn't exist."); } if (lineItem.quantity == 0) { throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Line item's quantity should greater than zero."); } for (ulong i = 0; i < lineItem.quantity; i++) { lineItemsTotal += lineItem.valuePerItem; } } if (lineItemsTotal != payload.amount) { throw new HttpStatusException( HttpStatus.BAD_REQUEST, "Total of all line items doesn't equal the transaction's total." ); } } }