package com.restapi.controllers import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.JsonDeserializer import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.restapi.domain.* import com.restapi.domain.Session.currentUser import com.restapi.domain.Session.database import com.restapi.domain.Session.findDataModelByEntityAndUniqId import com.restapi.integ.Scripting import io.ebean.CallableSql import io.ebean.RawSqlBuilder import io.javalin.http.* import org.slf4j.LoggerFactory import java.io.FileInputStream import java.sql.Types import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime import java.time.format.DateTimeFormatter enum class QueryParamType { STRING, NUMBER, DATETIME, DATE } data class RawQuery( val sql: String, val params: Map ) data class QueryById( val params: List ) @JsonDeserialize(using = QueryByIdParamsDeSerializer::class) sealed class QueryParam { data class Simple(val simple: String) : QueryParam() data class Complex(val type: QueryParamType, val value: String) : QueryParam() { fun getValueComplex(): Any { return when (type) { QueryParamType.STRING -> value QueryParamType.NUMBER -> if (value.matches(Regex("\\d+"))) value.toLong() else value.toDouble() QueryParamType.DATETIME -> LocalDateTime.parse( value, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") ) QueryParamType.DATE -> LocalDate.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd")) } } } fun getValue(): Any { return when (this) { is Complex -> getValueComplex() is Simple -> simple else -> {} } } } class QueryByIdParamsDeSerializer : JsonDeserializer() { override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): QueryParam { val node = p.readValueAsTree() return if (node.isTextual) { QueryParam.Simple(node.asText()) } else { QueryParam.Complex( QueryParamType.valueOf(node.get("type").textValue()), node.get("value").textValue(), ) } } } enum class ResultType { INTEGER, DECIMAL, STRING, DATETIME, ARRAY, OBJECT } data class RejectAction(val reason: String) data class StoredProcedure(val input: Map, val output: Map = hashMapOf()) object Entities { private val logger = LoggerFactory.getLogger("Entities") fun delete(ctx: Context) { val e = database.findDataModelByEntityAndUniqId(ctx.pathParam("entity"), ctx.pathParam("id")) e.deletedBy = Session.currentUser() e.deletedOn = LocalDateTime.now() e.update() e.delete() } fun patch(ctx: Context) { val e = database.findDataModelByEntityAndUniqId(ctx.pathParam("entity"), ctx.pathParam("id")) val pv = ctx.bodyAsClass>() pv.forEach { (key, value) -> e.data[key] = value; } e.update() } fun update(ctx: Context) { val purgeExisting = ctx.queryParam("purge")?.toBooleanStrictOrNull() == true val e = database.findDataModelByEntityAndUniqId(ctx.pathParam("entity"), ctx.pathParam("id")) val newData = ctx.bodyAsClass>() if (purgeExisting) { e.data.clear(); } e.data.putAll(newData) e.update() } fun action(ctx: Context) {} fun approve(ctx: Context) { approveOrReject(ctx, ApprovalStatus.APPROVED) } fun reject(ctx: Context) { approveOrReject(ctx, ApprovalStatus.REJECTED) } private fun approveOrReject(ctx: Context, rejected: ApprovalStatus) { val e = database.findDataModelByEntityAndUniqId(ctx.pathParam("entity"), ctx.pathParam("id")) val reject = ctx.bodyAsClass() e.approvalStatus = rejected e.comments.add(Comments(text = reject.reason, by = currentUser())) e.save() } fun executeScript(ctx: Context) { val name = ctx.pathParam("name") val file = ctx.pathParam("file") val params = ctx.bodyAsClass>() ctx.json( Scripting.execute(file, name, params) ) } fun executeStoredProcedure(ctx: Context) { val name = ctx.pathParam("name") val sp = ctx.bodyAsClass() val inputParams = sp.input.entries.toList() val outputParams = sp.output.entries.toList() val placeholders = (0..inputParams.size + 1).joinToString(",") { "?" } val sql = "{call $name($placeholders)}" val cs: CallableSql = database.createCallableSql(sql) inputParams.forEachIndexed { index, entry -> cs.setParameter(index + 1, entry.value) } cs.setParameter(inputParams.size + 1, Session.currentTenant()) outputParams.forEachIndexed { idx, entry -> when (entry.value) { ResultType.INTEGER -> cs.registerOut(idx + 1, Types.INTEGER) ResultType.DECIMAL -> cs.registerOut(idx + 1, Types.DOUBLE) ResultType.STRING -> cs.registerOut(idx + 1, Types.VARCHAR) ResultType.DATETIME -> cs.registerOut(idx + 1, Types.DATE) ResultType.ARRAY -> cs.registerOut(idx + 1, Types.ARRAY) ResultType.OBJECT -> cs.registerOut(idx + 1, Types.JAVA_OBJECT) } } val done = database.execute(cs) val output = outputParams.mapIndexed { index, entry -> Pair(entry.key, cs.getObject(index + 1)) }.toMap() ctx.json( mapOf( "done" to done, "output" to output ) ) } fun sqlQueryRaw(ctx: Context) { val sql = ctx.bodyAsClass() logger.warn("running sql ${sql.sql}, with params ${sql.params}") ctx.json( database.find(DataModel::class.java) .setRawSql( RawSqlBuilder.parse(sql.sql).create() ).apply { sql.params.forEach { (t, u) -> setParameter(t, u.getValue()) } } .findList() ) } fun sqlQueryById(ctx: Context) { val sql = ctx.bodyAsClass() val sqlId = ctx.pathParam("id") logger.warn("running sqlId $sqlId, with params ${sql.params}") val entity = ctx.pathParam("entity") val query = database.find(SqlModel::class.java) .where() .eq("entityName", entity) .eq("sqlId", sqlId) .findOne() ?: throw NotFoundResponse("sql not found for $entity, $sqlId") ctx.json( database.find(DataModel::class.java) .setRawSql(RawSqlBuilder.parse(query.sql).create()) .apply { sql.params.forEachIndexed { index, entry -> setParameter(index + 1, entry.getValue()) } } .findList() ) } fun view(it: Context) { database.save( AuditLog().apply { auditType = AuditType.VIEW entity = it.pathParam("entity") uniqueIdentifier = it.pathParam("id") } ) it.json( database.findDataModelByEntityAndUniqId(it.pathParam("entity"), it.pathParam("id")) ) } fun create(ctx: Context) { val entity = ctx.pathParam("entity") //may be approval flow is configured? val setupEntity = database.find(EntityModel::class.java) .where() .eq("name", entity) .findOne() Session.creatSeq(entity) val dataModel = ctx.bodyAsClass().apply { this.entityName = entity if (this.uniqueIdentifier.isEmpty()) { this.uniqueIdentifier = Session.nextUniqId(entity) } this.approvalStatus = ApprovalStatus.APPROVED } database.save( dataModel.apply { if (setupEntity != null) { val allowedFields = setupEntity.allowedFields.map { it.lowercase() } if (allowedFields.isNotEmpty()) { val moreFields = dataModel.data.keys.map { it.lowercase() }.filter { !allowedFields.contains(it) } if (moreFields.isNotEmpty()) { logger.warn("Data Keys = ${dataModel.data.keys} is more than $allowedFields, extra fields = $moreFields") throw BadRequestResponse("data contains more fields than allowed") } setupEntity.allowedFieldTypes.forEach { (key, expectedType) -> val valueFromUser = dataModel.data[key] ?: return@forEach val isDate = expectedType.equals("date", ignoreCase = true) val isDateTime = expectedType.equals("datetime", ignoreCase = true) val isTime = expectedType.equals("time", ignoreCase = true) if (isDate || isDateTime || isTime) { //this should be a string of a particular format if (valueFromUser !is String) { throw BadRequestResponse("field $key, is of type ${valueFromUser.javaClass.simpleName} expected $expectedType") } else { val dtPattern = Regex("^\\d{4}-\\d{2}-\\d{2}$") val dtmPattern = Regex("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}$") val timePattern = Regex("^\\d{2}:\\d{2}$") if (isDate && !dtPattern.matches(valueFromUser) && !isValidDate(valueFromUser) ) { throw BadRequestResponse("field $key, is of type ${valueFromUser.javaClass.simpleName} expected $expectedType") } if (isDateTime && !dtmPattern.matches(valueFromUser) && !isValidDateTime(valueFromUser) ) { throw BadRequestResponse("field $key, is of type ${valueFromUser.javaClass.simpleName} expected $expectedType") } if (isTime && !timePattern.matches(valueFromUser) && !isValidTime(valueFromUser) ) { throw BadRequestResponse("field $key, is of type ${valueFromUser.javaClass.simpleName} expected $expectedType") } } } if (valueFromUser.javaClass.simpleName != expectedType) { throw BadRequestResponse("field $key, is of type ${valueFromUser.javaClass.simpleName} expected $expectedType") } } } if (!setupEntity.preSaveScript.isNullOrEmpty()) { val ok = Scripting.execute(setupEntity.preSaveScript!!, "preSave", this) as Boolean if (!ok) { throw BadRequestResponse("PreSave Failed") } } if (setupEntity.approvalLevels > 0) { this.approvalStatus = ApprovalStatus.PENDING this.requiredApprovalLevels = setupEntity.approvalLevels } } } ) if (setupEntity != null && !setupEntity.postSaveScript.isNullOrEmpty()) { Scripting.execute(setupEntity.postSaveScript!!, "postSave", dataModel) } database.save( AuditLog().apply { auditType = AuditType.CREATE this.entity = entity uniqueIdentifier = dataModel.uniqueIdentifier this.data = dataModel.data } ) } private fun isValidDate(f: String) = try { LocalDate.parse(f, DateTimeFormatter.ofPattern("yyyy-MM-dd")) true } catch (e: Exception) { false } private fun isValidDateTime(f: String) = try { LocalDateTime.parse(f, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) true } catch (e: Exception) { false } private fun isValidTime(f: String) = try { LocalTime.parse(f, DateTimeFormatter.ofPattern("HH:mm")) true } catch (e: Exception) { false } } data class SequenceNumber(val number: String) object PurchaseOrderCtrl { fun getNextNum(ctx: Context) { val prefix = "PO/" val cnt = (database.find(PurchaseOrder::class.java) .findCount() + 1) .toString() .padStart(6, '0') val seq = SequenceNumber(prefix + cnt) ctx.json(seq).status(HttpStatus.OK) } fun get(ctx: Context) { val id = ctx.pathParam("id") val po = database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("po not found for $id") ctx.json(po).status(HttpStatus.OK) } data class PF(val common: CommonFilters, val poFilters: POFilters) fun getAll(ctx: Context) { val filters = ctx.bodyAsClass() val pos = searchPos(filters.common, filters.poFilters) val excel = ctx.queryParam("excel") if (excel != null) { exportPos(pos) val inputStream = FileInputStream("./excel/Pos.xls") ctx.result(inputStream).status(HttpStatus.OK) } else { ctx.json(pos).status(HttpStatus.OK) } } fun create(ctx: Context) { val po = ctx.bodyAsClass() val prods = po.products if (prods.isEmpty()) { ctx.json(mapOf("error" to "empty product list")).status(HttpStatus.BAD_REQUEST) return } database.save(po) ctx.json(po).status(HttpStatus.CREATED) } fun createBatch(ctx: Context) { val pos = ctx.bodyAsClass>() val txn = database.beginTransaction() try { txn.isBatchMode = true for (po in pos) database.save(po) txn.commit() ctx.status(HttpStatus.CREATED).result("POS Created") } catch (e: Exception) { txn.rollback() ctx.status(HttpStatus.INTERNAL_SERVER_ERROR).result("Pos Creation failed" + e.message) } finally { txn.end() } ctx.result("pos batch created").status(HttpStatus.CREATED) } fun approve(ctx: Context) { val id = ctx.pathParam("id") val po = database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("po not found for $id") po.approvalStatus = ApprovalStatus.APPROVED po.save() ctx.json(po).status(HttpStatus.CREATED) //reject all other pos pertaining to the same tx ?? } fun reject(ctx: Context) { val id = ctx.pathParam("id") val po = database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("po not found for $id") po.apply { approvalStatus = ApprovalStatus.REJECTED save() } ctx.json(po).status(HttpStatus.CREATED) } fun quoteReference(ctx: Context) { //gets the quote reference on which this po is based on val id = ctx.pathParam("id") val quote = database.find(Quotation::class.java) .where() .eq("referenceQuotation", id) ?: throw NotFoundResponse("reference quotation not found for po $id") ctx.json(quote) } fun update(ctx: Context) { val id = ctx.pathParam("id").toLong() val po = database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("po not found for $id") val updatedPo = ctx.bodyAsClass() po.patchValues(updatedPo) po.update() ctx.json(po).status(HttpStatus.OK) } fun delete(ctx: Context) { val id = ctx.pathParam("id") val po = database.find(PurchaseOrder::class.java, id) ?: throw NotFoundResponse("no po found with id $id") database.delete(po) ctx.status(HttpStatus.OK) } } object ProductCtrl { fun get(ctx: Context) { val id = ctx.pathParam("id") val product = database.find(Product::class.java) .where() .eq("sys_pk", id.toLong()) .findOne() ?: throw NotFoundResponse("Product not found for $id") ctx.json(product).status(HttpStatus.OK) } data class PF(val common: CommonFilters, val productFilters: ProductFilters) data class GetPrice(val productId: Long, val vendor: Any) fun getPrice(ctx: Context){ val gp = ctx.bodyAsClass() val vendor = database.find(Vendor::class.java, gp.vendor) ?: throw BadRequestResponse("vendor not found for ${gp.vendor}") val product = database.find(Product::class.java, gp.productId) ?: throw BadRequestResponse("product not found for ${gp.productId}") val poProduct = database.find(PurchaseOrder::class.java) .where() .eq("vendor", vendor) .findList() .flatMap { it.products } .firstOrNull { it.productId == product.sysPk } ctx.json( poProduct ?: throw BadRequestResponse("price not found for this vendor and product") ) } fun getAll(ctx: Context) { val filters = ctx.bodyAsClass() val prods = searchProducts(filters.common, filters.productFilters) val excel = ctx.queryParam("excel") if (excel != null) { exportProds(prods) val inputStream = FileInputStream("./excel/Products.xls") ctx.result(inputStream).status(HttpStatus.OK) } else { ctx.json(prods).status(HttpStatus.OK) } } fun create(ctx: Context) { val product = ctx.bodyAsClass() database.save(product) ctx.json(product).status(HttpStatus.CREATED) } fun delete(ctx: Context) { val id = ctx.pathParam("id") val prod = database.find(Product::class.java, id) ?: throw NotFoundResponse("no product found with id $id") database.delete(prod) ctx.status(HttpStatus.OK) } fun patch(ctx: Context) { val id = ctx.pathParam("id") val patchValues = ctx.bodyAsClass>() database.beginTransaction().use { patchValues.entries.forEach { en -> val key = en.key val value = en.value database.sqlUpdate("update products set $key = ? where id = ?").apply { setParameter(1, value) setParameter(2, id) execute() } } it.commit() } } fun update(ctx: Context) { val id = ctx.pathParam("id").toLong() val product = database.find(Product::class.java, id) ?: throw NotFoundResponse("product not found for $id") val updatedProduct = ctx.bodyAsClass() product.patchValues(updatedProduct) product.update() ctx.json(product).status(HttpStatus.OK) } @JvmStatic fun main(args: Array) { val patchValues = mapOf("name" to 1) val id = 1; database.beginTransaction().use { patchValues.entries.forEach { en -> val key = en.key val value = en.value database.sqlUpdate("update product set $key = ? where sys_pk = ?").apply { setParameter(1, value) setParameter(2, id) execute() } } it.commit() } } } object QuotationCtrl { fun getNextNum(ctx: Context) { val prefix = "QUOTE/" val cnt = database.find(Quotation::class.java) .findCount() .toString() .padStart(6, '0') val seq = SequenceNumber(prefix + cnt) ctx.json(seq).status(HttpStatus.OK) } fun get(ctx: Context) { val id = ctx.pathParam("id") val quote = database.find(Quotation::class.java, id) ?: throw NotFoundResponse("quote not found for $id") ctx.status(HttpStatus.OK) ctx.json(quote) } data class QF(val common: CommonFilters, val quoteFilters: QuoteFilters) fun getAll(ctx: Context) { val filters = ctx.bodyAsClass() val excel: String? = ctx.queryParam("excel") val quotes = searchQuotes(filters.common, filters.quoteFilters) if (excel != null) { exportQuotations(quotes) val inputStream = FileInputStream("./excel/Quotes.xls") ctx.result(inputStream).status(HttpStatus.OK) } else { ctx.json(quotes).status(HttpStatus.OK) } } fun create(ctx: Context) { val quote = ctx.bodyAsClass() //validation val prods = quote.products if (prods.isEmpty()) { ctx.json(mapOf("error" to "empty product list")).status(HttpStatus.BAD_REQUEST) return } database.save(quote) ctx.json(quote).status(HttpStatus.CREATED) } fun createBatch(ctx: Context) { val quotes = ctx.bodyAsClass>() val txn = database.beginTransaction() try { txn.isBatchMode = true for (quote in quotes) database.save(quote) txn.commit() ctx.status(HttpStatus.CREATED).result("Quotes Created") } catch (e: Exception) { txn.rollback() ctx.status(HttpStatus.INTERNAL_SERVER_ERROR).result("Quotes Creation failed" + e.message) } finally { txn.end() } ctx.result("Quotes batch created").status(HttpStatus.CREATED) } fun delete(ctx: Context) { val id = ctx.pathParam("id") val quote = database.find(Quotation::class.java, id) ?: throw NotFoundResponse("no quote found with id $id") database.delete(quote) ctx.status(HttpStatus.OK) } fun update(ctx: Context) { val id = ctx.pathParam("id").toLong() val quote = database.find(Quotation::class.java, id) ?: throw NotFoundResponse("quote not found for $id") val updatedQuote = ctx.bodyAsClass() quote.patchValues(updatedQuote) quote.update() ctx.json(quote).status(HttpStatus.OK) } } object DocumentCtrl { fun get(ctx: Context) { val id = ctx.pathParam("id") val doc = database.find(Document::class.java, id) ?: throw NotFoundResponse("no doc found with id $id") ctx.status(HttpStatus.OK) ctx.json(doc) } fun create(ctx: Context) { val doc = ctx.bodyAsClass() database.save(doc) ctx.status(HttpStatus.CREATED) ctx.json(doc) } fun print(ctx: Context) { //would be handled in the frontend ?? } fun delete(ctx: Context) { val id = ctx.pathParam("id") val doc = database.find(Document::class.java, id) ?: throw NotFoundResponse("no document found with id $id") database.delete(doc) ctx.status(HttpStatus.OK) } fun getWithRefId(ctx: Context) { //fetches a particular doc (po, quote) with ref id val refId = ctx.pathParam("refId") val doc = database.find(Document::class.java) .where() .eq("typeOfDoc", DocType.valueOf(ctx.pathParam("type"))) .eq("refIdOfDoc", refId) ?: throw NotFoundResponse("no doc found for refId $refId") ctx.status(HttpStatus.OK) ctx.json(doc) } } object VendorCtrl { val logger = LoggerFactory.getLogger("Vendor") fun get(ctx: Context) { val id = ctx.pathParam("id").toLong() val vendor = database.find(Vendor::class.java, id) ?: throw NotFoundResponse("no vendor found with id $id") ctx.status(HttpStatus.OK) ctx.json(vendor) } data class VF(val common: CommonFilters, val vendorFilters: VendorFilters) fun getAll(ctx: Context) { val filters = ctx.bodyAsClass() val excel: String? = ctx.queryParam("excel") val vendors = searchVendors(filters.common, filters.vendorFilters) if (excel !== null) { exportVendors(vendors) val inputStream = FileInputStream("./excel/VendorList.xls") ctx.result(inputStream).status(HttpStatus.OK) } else { ctx.json(vendors).status(HttpStatus.OK) } } fun createBatch(ctx: Context) { val vendors = ctx.bodyAsClass>() database.saveAll(vendors) } fun create(ctx: Context) { val vendor = ctx.bodyAsClass() database.save(vendor) ctx.status(HttpStatus.CREATED) ctx.json(vendor) } fun update(ctx: Context) { val id = ctx.pathParam("id").toLong() val vendor = database.find(Vendor::class.java, id) ?: throw NotFoundResponse("vendor not found for $id") val updatedVendor = ctx.bodyAsClass() vendor.patchValues(updatedVendor) vendor.update() ctx.json(vendor).status(HttpStatus.OK) } fun delete(ctx: Context) { val id = ctx.pathParam("id") val vendor = database.find(Vendor::class.java, id) ?: throw NotFoundResponse("no vendor found with id $id") database.delete(vendor) ctx.status(HttpStatus.OK) } fun getQuotes(ctx: Context) { val id = ctx.pathParam("id").toLong() val quotes = database.find(Quotation::class.java) .where() .eq("vendor", database.find(Vendor::class.java, id) ?: throw NotFoundResponse("vendor not found for $id")) .findList() ctx.json(quotes).status(HttpStatus.OK) } fun getPos(ctx: Context) { val id = ctx.pathParam("id").toLong() val pos = database.find(PurchaseOrder::class.java) .where() .eq("vendor", database.find(Vendor::class.java, id) ?: throw NotFoundResponse("vendor not found for $id")) .findList() ctx.json(pos).status(HttpStatus.OK) } fun rate(ctx: Context) { val id = ctx.pathParam("id") val rating1 = ctx.pathParam("rating").toDouble() database.find(Vendor::class.java, id)?.let { it.rating = rating1 it.save() } ?: throw NotFoundResponse("vendor not found for id $id") ctx.result("rating changed").status(HttpStatus.OK) } } object RequestForQuote { fun create(ctx: Context) { val rfq = ctx.bodyAsClass() database.save(rfq) //ctx.result("request for quote created") //ctx.json(rfq) ctx.status(HttpStatus.CREATED) ctx.json("asss") } fun get(ctx: Context) { val id = ctx.pathParam("id") val rfq = database.find(ReqForQuote::class.java, id) ?: throw NotFoundResponse("request for quote not found for id $id") ctx.status(HttpStatus.OK) ctx.json(rfq) } fun update(ctx: Context) { //shuld we compare the new body fields with preexisting ones and prepare a sql query to update those fields?? } } object PaymentCtrl { fun create(ctx: Context) { val pmt = ctx.bodyAsClass(Payment::class.java) //update the status of invoices pertaining to payment.vendor val vendors: List? = pmt.vendor?.let { listOf(it.sysPk) } val invoices = searchInvoices( CommonFilters(sortBy = "date", sortAsc = true, vendor = vendors), InvoiceFilters(status = InvoiceStatus.PAID_FULL) ) // println(invoices) //pmt.invoicesAffected = mutableMapOf() var totalAmount = pmt.amount var totalDeduct = 0.0 for (invoice in invoices) { val deduct = Math.min(totalAmount, invoice.totalAmount) invoice.totalAmount -= deduct totalDeduct += deduct totalAmount -= deduct if (invoice.totalAmount <= 0.0) { invoice.status = InvoiceStatus.PAID_FULL } else { invoice.status = InvoiceStatus.PAID_SOME } //println(invoice) database.update(invoice) pmt.invoicesAffected?.put(invoice.sysPk, deduct) println(pmt.invoicesAffected) if (totalAmount <= 0.0) break } pmt.amountDeducted = totalDeduct pmt.excessAmount = pmt.amount - pmt.amountDeducted!! database.save(pmt) ctx.json(pmt).status(HttpStatus.CREATED) } fun delete(ctx: Context) { val id = ctx.pathParam("id").toLong() val pmt = database.find(Payment::class.java, id) ?: throw NotFoundResponse("No payment found for this id $id") val invoiceDeductMap = pmt.invoicesAffected for (entry in invoiceDeductMap?.entries!!.iterator()) { val inv = database.find(Invoice::class.java, entry.key) ?: throw NotFoundResponse("No invoice found for $entry.key") inv.totalAmount += entry.value inv.status = InvoiceStatus.PAID_SOME database.update(inv) } database.delete(pmt) ctx.json(pmt).status(HttpStatus.OK) } fun get(ctx: Context) { val id = ctx.pathParam("id") val pmt = database.find(Payment::class.java, id) ?: throw NotFoundResponse("No payment found for this id $id") ctx.json(pmt).status(HttpStatus.OK) } data class PMTF(val common: CommonFilters, val paymentFilters: PaymentFilters) fun getAll(ctx: Context) { val filters = ctx.bodyAsClass() val payments = searchPayments(filters.common, filters.paymentFilters) // println(payments) val excel = ctx.queryParam("excel") if (excel !== null) { exportPayments(payments) val inputStream = FileInputStream("./excel/Payments.xls") ctx.result(inputStream).status(HttpStatus.OK) } else { ctx.json(payments).status(HttpStatus.OK) } } fun update(ctx: Context) { val id = ctx.pathParam("id").toLong() val pmt = database.find(Payment::class.java, id) ?: throw NotFoundResponse("payment not found for $id") val updatedPayment = ctx.bodyAsClass() pmt.patchValues(updatedPayment) pmt.update() ctx.json(pmt).status(HttpStatus.OK) } } object InvoiceCtrl { fun create(ctx: Context) { val invoice = ctx.bodyAsClass() database.save(invoice) ctx.json(invoice).status(HttpStatus.CREATED) } fun get(ctx: Context) { val id = ctx.pathParam("id").toLong() val invoice = database.find(Invoice::class.java, id) ?: throw NotFoundResponse("No invoice found with id $id") ctx.json(invoice).status(HttpStatus.OK) } data class INVF(val common: CommonFilters, val invoiceFilters: InvoiceFilters) fun getAll(ctx: Context) { val filters = ctx.bodyAsClass() val invoices = searchInvoices(filters.common, filters.invoiceFilters) val excel = ctx.queryParam("excel") if (excel !== null) { exportInvoices(invoices) val inputStream = FileInputStream("./excel/Invoices.xls") ctx.result(inputStream).status(HttpStatus.OK) } else { ctx.json(invoices).status(HttpStatus.OK) } } fun update(ctx: Context) { val id = ctx.pathParam("id").toLong() val invoice = database.find(Invoice::class.java, id) ?: throw NotFoundResponse("invoice not found for $id") val updatedPayment = ctx.bodyAsClass() invoice.patchValues(updatedPayment) invoice.update() ctx.json(invoice).status(HttpStatus.OK) } fun getNextNum(ctx: Context) { val prefix = "INV/" val cnt = database.find(Invoice::class.java) .findCount() .toString() .padStart(6, '0') val seq = SequenceNumber(prefix + cnt) ctx.json(seq).status(HttpStatus.OK) } }