-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pos-print: add async receipt printing
- Loading branch information
Showing
18 changed files
with
842 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
pos-print/src/main/java/com/clouway/pos/print/adapter/db/PersistentReceiptRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package com.clouway.pos.print.adapter.db | ||
|
||
import com.clouway.pos.print.core.* | ||
import com.google.inject.Inject | ||
import com.google.inject.Provider | ||
import com.mongodb.client.MongoCollection | ||
import com.mongodb.client.MongoDatabase | ||
import com.mongodb.client.model.Filters.eq | ||
import com.mongodb.client.model.Updates.set | ||
import org.bson.Document | ||
|
||
/** | ||
* @author Tsvetozar Bonev ([email protected]) | ||
*/ | ||
class PersistentReceiptRepository @Inject constructor(private val database: Provider<MongoDatabase>) | ||
: ReceiptRepository { | ||
|
||
private val collectionName: String = "receipts" | ||
|
||
override fun save(receipt: Receipt): String { | ||
if (receipts().find(eq("receiptId", receipt.receiptId)).firstOrNull() != null) | ||
throw ReceiptAlreadyInQueueException() | ||
|
||
val receiptDoc = receipt.toDocument() | ||
|
||
receipts().insertOne(receiptDoc) | ||
|
||
return receipt.receiptId | ||
} | ||
|
||
override fun getStatus(receiptId: String): ReceiptStatus { | ||
val receiptDoc = receipts().find( | ||
eq("receiptId", receiptId)).firstOrNull() | ||
?: throw ReceiptNotInQueueException() | ||
|
||
return ReceiptStatus.valueOf(receiptDoc.getString("status")) | ||
} | ||
|
||
override fun remove(receiptId: String): Receipt { | ||
val receiptDoc = receipts().findOneAndDelete( | ||
eq("receiptId", receiptId)) | ||
?: throw ReceiptNotInQueueException() | ||
|
||
return receiptDoc.toReceipt() | ||
} | ||
|
||
override fun getByStatus(receiptStatus: ReceiptStatus): List<Receipt> { | ||
val receiptDocs = receipts().find(eq("status", receiptStatus.name)) | ||
|
||
val receiptList = mutableListOf<Receipt>() | ||
|
||
receiptDocs.forEach { | ||
receiptList.add(it.toReceipt()) | ||
} | ||
|
||
return receiptList | ||
} | ||
|
||
override fun finishPrinting(receiptId: String): Receipt { | ||
val receiptDoc = receipts().findOneAndUpdate( | ||
eq("receiptId", receiptId), | ||
set("status", ReceiptStatus.PRINTED.name)) | ||
?: throw ReceiptNotInQueueException() | ||
|
||
return receiptDoc.toReceipt() | ||
} | ||
|
||
override fun failPrinting(receiptId: String): Receipt { | ||
val receiptDoc = receipts().findOneAndUpdate( | ||
eq("receiptId", receiptId), | ||
set("status", ReceiptStatus.FAILED.name)) | ||
?: throw ReceiptNotInQueueException() | ||
|
||
return receiptDoc.toReceipt() | ||
} | ||
|
||
private fun receipts(): MongoCollection<Document> { | ||
return database.get().getCollection(collectionName) | ||
} | ||
} |
65 changes: 65 additions & 0 deletions
65
pos-print/src/main/java/com/clouway/pos/print/adapter/db/ReceiptConverterHelpers.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package com.clouway.pos.print.adapter.db | ||
|
||
import com.clouway.pos.print.core.Receipt | ||
import com.clouway.pos.print.core.ReceiptItem | ||
import com.clouway.pos.print.core.ReceiptStatus | ||
import org.bson.Document | ||
|
||
/** | ||
* Helpers providing conversion between mongo bson docs and | ||
* receipt domain objects. | ||
* | ||
* @author Tsvetozar Bonev ([email protected]) | ||
*/ | ||
|
||
@Suppress("UNCHECKED_CAST") | ||
fun Document.toReceipt(): Receipt { | ||
val receipt = Receipt.Builder() | ||
.withReceiptId(this.getString("receiptId")) | ||
.withAmount(this.getDouble("amount")) | ||
.currency(this.getString("currency")) | ||
.prefixLines(this["prefixLines"] as List<String>) | ||
.suffixLines(this["suffixLines"] as List<String>) | ||
|
||
val itemList = this["receiptItems"] as List<Document> | ||
|
||
itemList.forEach { | ||
receipt.addItem(it.toReceiptItem()) | ||
} | ||
|
||
return receipt.build() | ||
} | ||
|
||
fun Document.toReceiptItem(): ReceiptItem{ | ||
return ReceiptItem.newItem() | ||
.name(this.getString("name")) | ||
.price(this.getDouble("price")) | ||
.quantity(this.getDouble("quantity")) | ||
.build() | ||
} | ||
|
||
fun Receipt.toDocument(): Document{ | ||
val itemList = this.receiptItems | ||
|
||
val docList = mutableListOf<Document>() | ||
|
||
itemList.forEach { | ||
docList.add(it.toDocument()) | ||
} | ||
|
||
return Document() | ||
.append("receiptId", this.receiptId) | ||
.append("amount", this.amount) | ||
.append("prefixLines", this.prefixLines()) | ||
.append("suffixLines", this.suffixLines()) | ||
.append("currency", this.currency) | ||
.append("receiptItems", docList) | ||
.append("status", ReceiptStatus.PRINTING.name) | ||
} | ||
|
||
fun ReceiptItem.toDocument(): Document{ | ||
return Document() | ||
.append("name", this.name) | ||
.append("price", this.price) | ||
.append("quantity", this.quantity) | ||
} |
54 changes: 54 additions & 0 deletions
54
pos-print/src/main/java/com/clouway/pos/print/adapter/http/AsyncPrintService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.clouway.pos.print.adapter.http | ||
|
||
import com.clouway.pos.print.core.* | ||
import com.clouway.pos.print.transport.GsonTransport | ||
import com.google.inject.name.Named | ||
import com.google.sitebricks.At | ||
import com.google.sitebricks.headless.Reply | ||
import com.google.sitebricks.headless.Request | ||
import com.google.sitebricks.headless.Service | ||
import com.google.sitebricks.http.Get | ||
import com.google.sitebricks.http.Post | ||
import org.slf4j.LoggerFactory | ||
import javax.inject.Inject | ||
|
||
/** | ||
* @author Tsvetozar Bonev ([email protected]) | ||
*/ | ||
@Service | ||
@At("/v2/receipts/req/print") | ||
class AsyncPrintService @Inject constructor(private var repository: ReceiptRepository, | ||
private var queue: PrintQueue) { | ||
private val logger = LoggerFactory.getLogger(AsyncPrintService::class.java) | ||
|
||
@Post | ||
fun printReceipt(request: Request): Reply<*> { | ||
return try { | ||
val dto = request.read(ReceiptDTO::class.java).`as`(GsonTransport::class.java) | ||
val receiptId = repository.save(dto.receipt) | ||
|
||
queue.queue(PrintReceiptRequest(dto.receipt, dto.sourceIp, dto.fiscal)) | ||
|
||
logger.info("Receipt queued for printing") | ||
Reply.with(receiptId).ok() | ||
} catch (ex: ReceiptAlreadyInQueueException) { | ||
logger.error("Receipt is already in queue") | ||
Reply.saying<Int>().badRequest() | ||
} | ||
} | ||
|
||
@Get | ||
@At("/status/:id") | ||
fun getReceiptStatus(@Named("id") id: String): Reply<*> { | ||
return try { | ||
val receiptStatus = repository.getStatus(id) | ||
logger.info("Receipt status returned as ${receiptStatus.name}") | ||
Reply.with(receiptStatus).ok() | ||
}catch (ex: ReceiptNotInQueueException){ | ||
logger.error("Receipt not found") | ||
Reply.saying<Int>().notFound() | ||
} | ||
} | ||
|
||
internal data class ReceiptDTO(val sourceIp: String = "", val operatorId: String = "", val fiscal: Boolean = false, val receipt: Receipt = Receipt.newReceipt().build()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
pos-print/src/main/java/com/clouway/pos/print/core/BackgroundReceiptPrintWorker.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package com.clouway.pos.print.core | ||
|
||
import com.clouway.pos.print.printer.Status | ||
import com.google.common.util.concurrent.AbstractExecutionThreadService | ||
import com.google.inject.Inject | ||
import org.slf4j.LoggerFactory | ||
import java.io.IOException | ||
|
||
/** | ||
* @author Tsvetozar Bonev ([email protected]) | ||
*/ | ||
class BackgroundReceiptPrintWorker @Inject constructor(private var repository: ReceiptRepository, | ||
private var factory: PrinterFactory, | ||
private var notifier: PrintNotifier, | ||
private var queue: PrintQueue) | ||
: ReceiptPrintWorker, AbstractExecutionThreadService() { | ||
|
||
override fun run() { | ||
while (isRunning){ | ||
printReceipts() | ||
} | ||
} | ||
|
||
private val logger = LoggerFactory.getLogger(ReceiptPrintWorker::class.java) | ||
|
||
override fun shutDown() { | ||
logger.info("Stopping background printing service") | ||
|
||
super.shutDown() | ||
} | ||
override fun printReceipts() { | ||
while (queue.hasNext()) { | ||
val receiptWithSource = queue.next() | ||
println(receiptWithSource) | ||
val receipt = receiptWithSource.receipt | ||
|
||
var printer: ReceiptPrinter? = null | ||
|
||
try { | ||
printer = factory.getPrinter(receiptWithSource.sourceIp) | ||
|
||
val printResponse = if (receiptWithSource.isFiscal) printer.printFiscalReceipt(receipt) | ||
else printer.printReceipt(receipt) | ||
|
||
if (printResponse.warnings.contains(Status.FISCAL_RECEIPT_IS_OPEN) | ||
|| printResponse.warnings.contains(Status.NON_FISCAL_RECEIPT_IS_OPEN)) { | ||
logger.info("Receipt printing accepted") | ||
repository.finishPrinting(receipt.receiptId) | ||
} else { | ||
logger.info("Receipt printing rejected") | ||
repository.failPrinting(receipt.receiptId) | ||
} | ||
|
||
notifier.notifyPrinted(printResponse) | ||
|
||
}catch (ex: ReceiptNotInQueueException) { | ||
logger.warn("Receipt was not found in queue") | ||
}catch (ex: DeviceNotFoundException) { | ||
logger.warn("Device was not found") | ||
repository.failPrinting(receipt.receiptId) | ||
println("Receipt is ${receipt.receiptId}") | ||
notifier.notifyPrinted(PrintReceiptResponse(setOf(Status.GENERAL_ERROR))) | ||
}catch (ex: IOException){ | ||
logger.warn("Printer threw IO Exception") | ||
repository.failPrinting(receipt.receiptId) | ||
notifier.notifyPrinted(PrintReceiptResponse(setOf(Status.GENERAL_ERROR))) | ||
}finally { | ||
printer?.close() | ||
} | ||
} | ||
} | ||
|
||
override fun start() { | ||
logger.info("Starting background printing service") | ||
|
||
this.startAsync() | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
pos-print/src/main/java/com/clouway/pos/print/core/ConsolePrintNotifier.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.clouway.pos.print.core | ||
|
||
/** | ||
* @author Tsvetozar Bonev ([email protected]) | ||
*/ | ||
class ConsolePrintNotifier : PrintNotifier { | ||
override fun notifyPrinted(printResponse: PrintReceiptResponse) { | ||
Thread.sleep(1000) | ||
println(printResponse) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package com.clouway.pos.print.core | |
|
||
import com.clouway.pos.print.printer.FP705PrinterFactory | ||
import com.google.inject.AbstractModule | ||
import com.google.inject.Singleton | ||
|
||
/** | ||
* @author Martin Milev <[email protected]> | ||
|
@@ -10,5 +11,8 @@ class CoreModule : AbstractModule() { | |
|
||
override fun configure() { | ||
bind(PrinterFactory::class.java).to(FP705PrinterFactory::class.java) | ||
bind(PrintNotifier::class.java).to(ConsolePrintNotifier::class.java) | ||
bind(PrintQueue::class.java).to(InMemoryPrintQueue::class.java).`in`(Singleton::class.java) | ||
bind(ReceiptPrintWorker::class.java).to(BackgroundReceiptPrintWorker::class.java).`in`(Singleton::class.java) | ||
} | ||
} |
Oops, something went wrong.