Skip to content

Instantly share code, notes, and snippets.

@ANNASBlackHat
Last active February 13, 2024 08:40
Show Gist options
  • Save ANNASBlackHat/f8111fa7812466937286a151c9a08304 to your computer and use it in GitHub Desktop.
Save ANNASBlackHat/f8111fa7812466937286a151c9a08304 to your computer and use it in GitHub Desktop.
Generate Close Shift Recap
import 'dart:core';
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:collection/collection.dart';
import 'dart:math';
void main() {
// Create dummy data
SalesEntity sales = dummySales();
Outlet outlet = dummyOutlet();
Employee employee = Employee(name: 'Jane Doe');
List<CashRecapEntity> cashRecaps = generateDummyCashRecap();
List<TmpSalesEntity> pending = generateDummyTmpSales(5);
List<OperationalCost> operationalCost = generateDummyOperationalCost(2);
String? printerRule =
'{"sales":true,"paymentmedia":true,"groupsales":true,"refund":true,"time":true,"tax":true,"itemsales":true,"avgpaxbill":true,"fifo":true,"actual":true,"opcostdetail":true,"entertainincome":true}';
List<SubCategoryEntity> categories = generateDummySubCategories(2);
// Call the function with the dummy data
String cashRecapFormat = getCashRecapFormat(
cashRecaps,
outlet, // Make sure to replace outlet with actual outlet object
[sales], // Make sure to replace sales with actual sales list
pending,
operationalCost,
printerRule,
categories,
paperSize: 58,
watermark: "#REPRINT#",
openShifts: [], // Optionally provide openShifts if needed
isRecap: true, // Set isRecap flag as needed
);
// Print the formatted cash recap
print(cashRecapFormat);
}
String getCashRecapFormat(
List<CashRecapEntity> cashRecaps,
Outlet outlet,
List<SalesEntity> sales,
List<TmpSalesEntity> pending,
List<OperationalCost> op,
String? printerRule,
List<SubCategoryEntity> categories,
{int? paperSize = 58,
String? watermark,
List<OpenShiftEntity> openShifts = const [],
bool isRecap = false}) {
StringBuffer nota = StringBuffer("[CUT]");
int MAX = getMaxCharacter(paperSize);
nota.writeln();
RolePrinterClosing defaultRule = RolePrinterClosing(
sales: true, paymentmedia: true, groupsales: true, refund: true);
RolePrinterClosing rule = defaultRule; //printerRule != null
if (printerRule != null) {
try {
rule = RolePrinterClosing.fromJson(json.decode(printerRule));
} catch (e) {
print('error ${e}');
}
}
List<CashRecapEntity> cashRecapSorted = cashRecaps
..sort((a, b) => a.timeCreated.compareTo(b.timeCreated));
String dateFormat = "dd/MM/yyyy";
if (rule.time) {
dateFormat += " HH:mm";
}
String date = cashRecapSorted.isNotEmpty
? cashRecapSorted.first.timeCreated.dateTimeFormat(dateFormat)
: DateFormat(dateFormat).format(DateTime.now());
try {
List<String> cashier = [];
cashRecaps.forEach((it) {
if (!cashier.contains(it.employeeName.toUpperCase())) {
cashier.add(it.employeeName.toUpperCase());
}
});
String sw = "#".safeRepeat(watermark?.split("\n").length ?? 0);
String title =
isRecap ? "LAPORAN REKAP TUTUP KASIR" : "LAPORAN TUTUP KASIR";
nota.writeln(outlet.name.trim().center(MAX));
nota.writeln(outlet.receiptAddress?.center(MAX));
nota.writeln("=".loop(MAX));
nota.writeln("${sw}${title}${sw}".center(MAX));
nota.writeln("Kasir : ${cashier.join(', ')}".width(MAX));
nota.writeln("Tanggal : $date".width(MAX));
nota.writeln();
List<SalesEntity> salesSuccess =
sales.where((p) => p.status.toLowerCase() == "success").toList();
List<SalesEntity> netSales = salesSuccess
.where((p) =>
p.payment.toUpperCase().contains("CASH") ||
p.payment.toUpperCase().contains("CARD") ||
p.payment.toUpperCase().contains("PIUTANG"))
.toList();
List<Payment> payments = salesSuccess.expand((it) => it.payments).toList();
int totalNetSales = payments
.where((it) =>
it.method.toLowerCase() == "cash" ||
it.method.toLowerCase() == "card" ||
it.method.toLowerCase() == "piutang")
.map((it) => it.total)
.fold(0, (prev, elem) => prev + elem);
int netSalesTotal = netSales
.map((it) => it.grandTotal)
.fold(0, (prev, elem) => prev + elem);
List<Order> orders = salesSuccess
.expand((it) => it.orderList)
.where((it) => !it.isItemVoid)
.toList();
List<Order> ordersVoid = salesSuccess
.expand((it) => it.orderList)
.where((it) => it.isItemVoid)
.toList();
List<Order> ordersCombined = (rule.itemsales || rule.groupsales)
? orders.expand((it) => it.extra).toList()
: [];
ordersCombined.addAll(orders);
List<Order> ordersVoidCombined = (rule.itemsales || rule.groupsales)
? ordersVoid.expand((it) => it.extra).toList()
: [];
ordersVoidCombined.addAll(ordersVoid);
List<Promotion> promotions = orders
.where((it) => it.promotion != null)
.map((it) => it.promotion!)
.toList();
promotions.addAll(ordersVoid
.where((it) => it.promotion != null)
.map((it) => it.promotion!));
salesSuccess
.where((it) => it.promotions != null)
.expand((it) => it.promotions!)
.forEach((promo) {
promotions.add(
Promotion(name: promo.name, promotionValue: promo.promotionValue));
});
int totalPromotion = promotions
.map((it) => it.promotionValue)
.fold(0, (prev, elem) => prev + elem);
int discTotal = totalPromotion;
int allQty = 0;
netSales.forEach((sales) {
discTotal += sales.discount?.discountNominal ?? 0;
sales.orderList
.where((it) => !it.isItemVoid && it.discount.discountNominal > 0)
.forEach((o) => discTotal += o.discount.discountNominal);
sales.taxes?.forEach((t) {
if (t.category == Constant.TAX_CATEGORY_DISC) {
discTotal += t.total;
}
});
sales.orderList.forEach((order) {
int helper = order.isItemVoid ? -1 : 1;
allQty += (order.qty.abs() * helper);
order.extra.forEach((extra) => allQty += (extra.qty.abs() * helper));
});
});
nota.writeln(" SALES ".center(MAX, "="));
nota.write("Item Sales".width(MAX - 4 - 15));
nota.write(allQty.toCurrency().width(5));
nota.writeln((totalNetSales + discTotal).toCurrency().widthRight(15));
nota.write("Discount Sales".width(MAX - 15));
nota.writeln(discTotal.toCurrency().widthRight(15));
nota.writeln("-".loop(MAX));
nota.write("Net Sales".width(MAX - 15));
nota.writeln(totalNetSales.toCurrency().widthRight(15));
nota.writeln();
int totalMedia = 0;
int totalCard = 0;
int totalCash = 0;
if (rule.paymentmedia) nota.writeln(" MEDIA ".center(MAX, "="));
List<String> medias = ["Cash", "Card", "Piutang"];
medias.forEach((media) {
List<Payment> pay = payments
.where((it) => it.method.toLowerCase() == media.toLowerCase())
.toList();
int total =
pay.map((it) => it.total).fold(0, (prev, elem) => prev + elem);
if (media.toLowerCase() == "card") {
Map<int, List<Payment>> bankList =
groupBy(payments, (payment) => payment.bank!.bankId);
bankList.forEach((key, value) {
int totalBank =
value.map((it) => it.total).fold(0, (prev, elem) => prev + elem);
totalCard += totalBank;
if (rule.paymentmedia) {
nota.write(value.first.bank!.name.width(MAX - 15));
nota.writeln(totalBank.toCurrency().width(15));
}
});
}
if (media.toLowerCase() == "cash") totalCash += total;
totalMedia += total;
if (rule.paymentmedia) {
nota.write(media.width(MAX - 13));
nota.writeln(total.toCurrency().widthRight(13));
}
});
nota.writeln("-".loop(MAX));
nota.writeln("Grand Total : ".widthRight(MAX - 13) +
totalMedia.toCurrency().widthRight(13));
nota.writeln();
int allTax = 0;
if (rule.tax) {
nota.writeln(" TAX ".center(MAX, "="));
List<Tax> allTaxes = netSales.expand((it) => it.taxes!).toList();
List<Tax> taxServices = allTaxes
.where((it) =>
it.total > 0 &&
(it.category == "service" || it.category == "tax"))
.toList();
Map<String?, List<Tax>> groupedTaxes =
groupBy(taxServices, (tax) => tax.name);
groupedTaxes.forEach((key, value) {
int total =
value.map((it) => it.total).fold(0, (prev, elem) => prev + elem);
allTax += total;
nota.write(key?.width(MAX - 11));
nota.writeln(total.toCurrency().widthRight(11));
});
nota.writeln("-".loop(MAX));
nota.writeln("Grand Total : ".widthRight(MAX - 13) +
allTax.toCurrency().widthRight(13));
nota.writeln();
}
if (rule.itemsales) {
nota.writeln(" ITEM SALES ".center(MAX, "="));
int allTotal = 0;
int allQtyItemSales = 0;
ordersVoidCombined.forEach((it) {
it.extraType = it.extraType == "link" ? "link" : "";
});
categories.sortedBy((it) => it.name).forEach((category) {
List<Order> orderFilter = ordersCombined
.where((it) =>
it.product.productSubcategoryFkid == category.productCategoryId)
.toList();
if (orderFilter.isNotEmpty) {
StringBuffer items = StringBuffer();
orderFilter.forEach((it) {
it.extraType = it.extraType == "link" ? "link" : "";
});
List<Order> extraSorted =
orderFilter.sortedBy((it) => it.extraType!).toList();
Map<String, List<Order>> salesGroup = groupBy(extraSorted,
(it) => "${it.product.productDetailId}#${it.extraType}");
bool isFirstExtra = true;
salesGroup.forEach((prodId, orderList) {
int qty = orderList
.map((it) => it.qty)
.fold(0, (prev, elem) => prev + elem);
int subTotal = orderList
.map((it) => it.subTotal)
.fold(0, (prev, elem) => prev + elem);
int productId = int.parse(prodId.substring(0, prodId.indexOf("#")));
String extraType = prodId.substring(prodId.indexOf("#") + 1);
ordersVoidCombined
.where((voidOrder) =>
voidOrder.product.productDetailId == productId &&
voidOrder.extraType == extraType)
.forEach((voidOrder) {
qty -= (voidOrder.qty.abs());
subTotal -= (voidOrder.subTotal.abs());
});
if (qty > 0) {
if (extraType.isNotEmpty && isFirstExtra) {
isFirstExtra = false;
items.writeln(
"[${extraType == "link" ? "LINK MENU" : extraType.toUpperCase()}]"
.width(MAX));
}
items.write(orderList.first.product.name?.width(MAX - 5 - 11));
items.write(qty.toString().center(5));
items.writeln(subTotal.toCurrency().widthRight(11));
allTotal += subTotal;
allQtyItemSales += qty;
}
});
if (items.isNotEmpty) {
nota.writeln("#${category.name}".width(MAX));
nota.write(items);
}
}
});
nota.writeln("-" * MAX);
nota.writeln("Grand Total : ".widthRight(MAX - 5 - 11) +
allQtyItemSales.toString().center(5) +
allTotal.toCurrency().widthRight(11));
nota.writeln('\n');
}
if (rule.groupsales) {
int itemSalesTotal = 0;
int itemSalesQty = 0;
nota.writeln(" GROUP SALES ".center(MAX, "="));
categories.sortedBy((it) => it.name).forEach((category) {
List<Order> orderFilter = ordersCombined
.where((it) =>
it.product.productSubcategoryFkid ==
category.productCategoryId &&
!it.isItemVoid)
.toList();
int total = orderFilter
.map((it) => it.subTotal)
.fold(0, (prev, elem) => prev + elem);
int qty = orderFilter
.map((it) => it.qty)
.fold(0, (prev, elem) => prev + elem);
if (ordersVoidCombined.isNotEmpty) {
Map<int, List<Order>> salesGroup =
groupBy(orderFilter, (it) => it.product.productDetailId);
salesGroup.forEach((prodId, _) {
ordersVoidCombined
.where((it) => it.product.productDetailId == prodId)
.forEach((orderVoid) {
qty -= orderVoid.qty.abs();
total -= (orderVoid.subTotal.abs());
});
});
}
if (total > 0 || qty > 0) {
itemSalesQty += qty;
itemSalesTotal += total;
nota.write(category.name.width(MAX - 5 - 11));
nota.write(qty.toString().center(5));
nota.writeln(total.toCurrency().widthRight(11));
}
});
nota.writeln("-".loop(MAX));
nota.writeln("Grand Total : ".widthRight(MAX - 5 - 11) +
itemSalesQty.toCurrency().center(5) +
itemSalesTotal.toCurrency().widthRight(11));
nota.writeln('\n');
}
if (promotions.isNotEmpty) {
nota.writeln(" PROMOTIONS ".center(MAX, "="));
Map<String?, List<Promotion>> promotionGroup =
groupBy(promotions, (it) => it.name);
promotionGroup.forEach((name, promoList) {
int totalPromo = promoList
.map((it) => it.promotionValue)
.fold(0, (prev, elem) => (prev ?? 0) + (elem ?? 0));
nota.write(name?.width(MAX - 11));
nota.writeln(totalPromo.toCurrency().widthRight(11));
});
nota.writeln("-".loop(MAX));
nota.writeln("Grand Total".widthRight(MAX - 16) +
totalPromotion.toCurrency().widthRight(16));
nota.writeln('\n');
}
List<Tax> allTaxes = netSales.expand((it) => it.taxes!).toList();
int totalTax = allTaxes
.where((it) =>
it.total > 0 && (it.category == "service" || it.category == "tax"))
.map((it) => it.total)
.fold(0, (prev, elem) => prev + elem);
int totalOdb = netSalesTotal - totalTax + discTotal;
nota.writeln(" SALES RECAP ".center(MAX, "="));
nota.write("Total ODB".width(MAX - 13));
nota.writeln(totalOdb.toCurrency().widthRight(13));
nota.write("Discount".width(MAX - 13));
nota.writeln(discTotal.toCurrency().widthRight(13));
nota.write("After disc.".width(MAX - 13));
nota.writeln((totalOdb - discTotal).toCurrency().widthRight(13));
List<Tax> taxServices = allTaxes
.where((it) =>
it.total > 0 && (it.category == "service" || it.category == "tax"))
.toList();
Map<String?, List<Tax>> groupedTaxes =
groupBy(taxServices, (it) => it.name);
groupedTaxes.forEach((key, value) {
int total =
value.map((it) => it.total).fold(0, (prev, elem) => prev + elem);
nota.write(key?.width(MAX - 13));
nota.writeln(total.toCurrency().widthRight(13));
});
nota.writeln("\n\n");
int pendingBillTotal = cashRecaps
.map((it) => it.pendingBillTotal)
.fold(0, (prev, elem) => prev + elem);
if (pendingBillTotal > 0) {
nota.writeln(" PENDING BILL ".center(32, "="));
nota.writeln("Grand Total : ".widthRight(MAX - 16) +
pendingBillTotal.toCurrency().widthRight(11));
nota.writeln();
} else if (pending.isNotEmpty &&
date == DateFormat(dateFormat).format(DateTime.now())) {
nota.writeln(" PENDING BILL ".center(32, "="));
pendingBillTotal = 0;
int pendingBillQty = 0;
List<SalesEntity> salesPending = pending.map((e) {
return SalesEntity.fromJson(json.decode(e.sales));
}).toList();
List<Order> orderPending =
salesPending.expand((it) => it.orderList).toList();
Map<int, List<Order>> orderGroup =
groupBy(orderPending, (it) => it.product.productDetailId);
orderGroup.forEach((_, orderList) {
int totalQty = orderList
.map((it) => it.isItemVoid ? (it.qty.abs()) * -1 : it.qty)
.fold(0, (prev, elem) => prev + elem);
int totalSubtotal = orderList
.map((it) => it.isItemVoid ? (it.subTotal.abs()) * -1 : it.subTotal)
.fold(0, (prev, elem) => prev + elem);
nota.write(orderList.first.product.name?.width(MAX - 5 - 8));
nota.write(totalQty.toCurrency().width(5));
nota.writeln(totalSubtotal.toCurrency().widthRight(8));
pendingBillTotal += totalSubtotal;
pendingBillQty += totalQty;
});
nota.writeln("-".loop(MAX));
nota.writeln("Grand Total".widthRight(MAX - 5 - 11) +
pendingBillQty.toCurrency().center(5) +
pendingBillTotal.toCurrency().widthRight(11));
nota.writeln();
}
List<Order> salesVoid = salesSuccess
.expand((it) => it.orderList)
.where((it) => it.isItemVoid)
.toList();
if (salesVoid.isNotEmpty) {
nota.writeln(" VOID ".center(MAX, "="));
int subtotalExtra = 0;
salesVoid.forEach((v) {
nota.write(v.product?.name
?.width(MAX - 5 - 11)); // 3 spasi antara menu dan qty
nota.write(v.qty.toCurrency().center(5));
nota.writeln(((v.subTotal.abs())).toCurrency().widthRight(11));
v.extra.forEach((e) {
nota.write("- ${e.product.name}"
.width(MAX - 5 - 11)); // 3 spasi antara menu dan qty
nota.write(e.qty.toCurrency().center(5));
nota.writeln(((e.subTotal.abs())).toCurrency().widthRight(11));
subtotalExtra += (e.subTotal.abs());
});
});
int totalVoid = salesVoid
.map((it) => (it.subTotal.abs()))
.fold(0, (prev, elem) => (prev) + elem);
totalVoid -= subtotalExtra;
nota.writeln("-".loop(MAX));
nota.writeln("Grand Total : ".widthRight(MAX - 13) +
totalVoid.toCurrency().widthRight(13));
nota.writeln();
}
List<SalesEntity> refund =
sales.where((it) => it.status?.toLowerCase() == "refund").toList();
if (refund.isNotEmpty) {
nota.writeln(" REFUND ".center(MAX, "="));
refund.forEach((sale) {
int totalTax = sale.taxes
?.map((it) => it.total)
.fold(0, (prev, elem) => (prev ?? 0) + elem) ??
0;
int avgTax = totalTax ~/ (sale.orderList.length ?? 1);
sale.orderList.forEach((order) {
nota.write(order.product.name?.width(MAX - 5 - 10));
nota.write(order.qty.toString().center(5));
nota.writeln((order.subTotal + avgTax).toCurrency().widthRight(10));
order.extra.forEach((extra) {
nota.write(("- " + (extra.product.name ?? '')).width(MAX - 5 - 10));
nota.write(extra.qty.toString().center(5));
nota.writeln(extra.subTotal.toCurrency().widthRight(10));
});
});
});
int allRefund = refund
.map((it) => it.grandTotal)
.fold(0, (prev, elem) => prev + elem);
nota.writeln("-".loop(MAX));
List<Tax> taxesRefund = refund.expand((it) => it.taxes!).toList();
Map<String?, List<Tax>> taxesRefundGroup =
groupBy(taxesRefund, (p) => p.name);
taxesRefundGroup.forEach((taxName, taxValues) {
int taxTotal = taxValues
.map((it) => it.total)
.fold(0, (prev, elem) => prev + elem);
nota.write(taxName?.width(MAX - 10));
nota.writeln(taxTotal.toCurrency().widthRight(10));
});
nota.writeln("Grand Total : ".widthRight(MAX - 13) +
allRefund.toCurrency().widthRight(13));
nota.writeln();
}
if (rule.avgpaxbill) {
int allPax = salesSuccess
.map((it) => it.customersQty)
.fold(0, (prev, elem) => prev + elem);
int avgPax = allPax > 0 ? netSalesTotal ~/ allPax : 0;
nota.write("Total Pax".width(MAX - 8));
nota.writeln(allPax.toCurrency().widthRight(8));
nota.write("Avg Pax".width(MAX - 8));
nota.writeln(avgPax.toCurrency().widthRight(8));
nota.write("Total Bill".width(MAX - 8));
nota.writeln(salesSuccess.length.toCurrency().widthRight(8));
nota.writeln("Avg Bill".width(MAX - 10) +
(netSalesTotal ~/ (sales.isEmpty ? 1 : sales.length))
.toCurrency()
.widthRight(10));
nota.writeln();
}
if (rule.fifo) {
double earlyCash = openShifts
.map((it) => it.earlyCash)
.fold(0, (prev, elem) => prev + elem);
nota.writeln(" FIFO ".center(MAX, "="));
nota.write("Kas Awal".width(MAX - 10));
nota.writeln(earlyCash.toInt().toCurrency().widthRight(10));
int cashIn = 0;
nota.write("Kas Masuk".width(MAX - 11));
nota.writeln(cashIn.toCurrency().widthRight(11));
nota.write("Pendapatan Tunai".width(MAX - 13));
nota.writeln(totalCash.toCurrency().width(13));
nota.write("Pendapatan Kartu".width(MAX - 13));
nota.writeln(totalCard.toCurrency().width(13));
nota.write("Total Pendapatan".width(MAX - 10));
nota.writeln((totalCash + totalCard).toCurrency().widthRight(10));
nota.write("Kas Keluar".width(MAX - 9));
nota.writeln((0).toCurrency().widthRight(9));
nota.writeln("-".loop(MAX));
int sisa = totalCash + totalCard + earlyCash.toInt();
nota.writeln(
"Sisa : ".widthRight(MAX - 13) + sisa.toCurrency().widthRight(13));
nota.writeln();
}
if (rule.actual) {
int totalActualCash =
cashRecaps.map((it) => it.cash).fold(0, (prev, elem) => prev + elem);
int totalActualCard =
cashRecaps.map((it) => it.card).fold(0, (prev, elem) => prev + elem);
nota.writeln(" AKTUAL ".center(MAX, "="));
nota.write("Cash".width(MAX - 13));
nota.writeln(totalActualCash.toCurrency().widthRight(13));
nota.write("Card".width(MAX - 13));
nota.writeln(totalActualCard.toCurrency().widthRight(13));
nota.writeln("-".loop(MAX));
nota.writeln("Total : ".widthRight(MAX - 13) +
(totalActualCash + totalActualCard).toCurrency().widthRight(13));
nota.writeln('\n');
nota.writeln("SELISIH : ".widthRight(MAX - 13) +
((totalActualCash + totalActualCard) - (totalCash + totalCard))
.toCurrency()
.widthRight(13));
nota.writeln("\n\n");
}
int totalOP =
op.map((it) => it.total.toInt()).fold(0, (prev, elem) => prev + elem);
if (rule.opcostdetail && op.isNotEmpty) {
nota.writeln(" OPERATIONAL COST ".center(MAX, "="));
op.forEach((operationalCost) {
nota.write(operationalCost.opcostName.width(MAX - 13));
nota.writeln((operationalCost.total ?? 0).toCurrency().widthRight(13));
});
nota.writeln("\n\n");
}
nota.writeln(" CASHFLOW ".center(MAX, "="));
nota.write("Net Sales".width(MAX - 15));
nota.writeln((totalNetSales + discTotal).toCurrency().widthRight(15));
nota.write("Operational Cost".width(MAX - 15));
nota.writeln((totalOP).toCurrency().widthRight(15));
nota.writeln("-".loop(MAX));
nota.writeln("Total Cashflow : ".widthRight(MAX - 13) +
((totalNetSales + discTotal) - totalOP).toCurrency().widthRight(13));
nota.writeln();
nota.writeln(" PURCHASING ".center(MAX, "="));
nota.write("Operational Cost".width(MAX - 10));
nota.writeln((totalOP).toCurrency().widthRight(10));
nota.write("Purchase Cash".width(MAX - 10));
nota.writeln((0).toCurrency().widthRight(10));
nota.write("Purchase Hutang".width(MAX - 10));
nota.writeln((0).toCurrency().widthRight(10));
nota.writeln('\n');
int totalDutyMeals = salesSuccess
.where((it) => it.payment.toLowerCase().contains("duty meals"))
.map((it) => it.grandTotal)
.fold(0, (prev, elem) => prev + elem);
int totalCompliment = salesSuccess
.where((it) => it.payment.toLowerCase().contains("compliment"))
.map((it) => it.grandTotal)
.fold(0, (prev, elem) => prev + elem);
int allDiscTotal = totalPromotion;
int allVoucherTotal = 0;
salesSuccess.forEach((s) {
allDiscTotal += s.discount?.discountNominal ?? 0;
allVoucherTotal += s.discount?.voucherNominal ?? 0;
s.orderList.forEach((o) => allDiscTotal += o.discount.discountNominal);
s.taxes?.forEach((t) {
if (t.category == Constant.TAX_CATEGORY_DISC) {
allDiscTotal += t.total;
} else if (t.category == Constant.TAX_CATEGORY_VOUCHER) {
allVoucherTotal += t.total;
}
});
});
watermark?.split("\n").forEach((it) => nota.writeln(it.width(MAX)));
nota.writeln();
if (rule.entertainincome) {
List<int> typesFree = [
Constant.PROMO_TYPE_FREE_MEMBER,
Constant.PROMO_TYPE_FREE
];
List<int> typeVoucher = [
Constant.PROMO_TYPE_DISCOUNT_VOUCHER,
Constant.PROMO_TYPE_FREE_VOUCHER,
Constant.PROMO_TYPE_SPECIAL_PRICE_VOUCHER
];
int allFree = 0;
int promoVoucher = 0;
(orders + ordersVoid).forEach((order) {
int helper = order.isItemVoid ? -1 : 1;
if (typesFree.contains(order.promotion?.pomotionTypeFkid)) {
allFree += (order.promotion?.promotionValue ?? 0).abs() * helper;
} else if (typeVoucher.contains(order.promotion?.pomotionTypeFkid)) {
promoVoucher += (order.promotion?.promotionValue ?? 0).abs() * helper;
}
if ((order.discount.discountNominal).abs() >= (order.subTotal).abs()) {
allFree += (order.subTotal).abs() * helper;
}
});
// allDiscTotal is containing free and promo voucher, so we have to subtract it
int discAll = allDiscTotal - allFree - promoVoucher;
int voucher = allVoucherTotal + promoVoucher;
int entertainTotal = totalDutyMeals + totalCompliment + discAll + voucher;
nota.writeln(" ENTERTAIN INCOME ".center(MAX, "="));
nota.write("Compliment".width(MAX - 9));
nota.writeln(totalCompliment.toCurrency().widthRight(9));
nota.write("Discount All".width(MAX - 9));
nota.writeln(discAll.toCurrency().widthRight(9));
nota.write("Free".width(MAX - 9));
nota.writeln(allFree.toCurrency().widthRight(9));
nota.write("Voucher".width(MAX - 9));
nota.writeln(voucher.toCurrency().widthRight(9));
nota.write("Duty Meals".width(MAX - 9));
nota.writeln(totalDutyMeals.toCurrency().widthRight(9));
nota.writeln('\n');
nota.write("Entertain Income +".width(MAX - 11));
nota.writeln(
(entertainTotal + netSalesTotal).toCurrency().widthRight(11));
nota.writeln("Net Sales".width(MAX));
nota.writeln('\n\n\n');
}
} catch (e) {
print("Generate Nota tutup kasir error $e");
}
return nota.toString();
}
int getMaxCharacter(int? paperSize) {
switch (paperSize) {
case 75:
return 40;
case 80:
return 48;
default:
return 32;
}
}
class Constant {
static const String TYPE_NOMINAL = "nominal";
static const String TYPE_PERCENT = "percentage";
static const int PROMO_TYPE_SPECIAL_PRICE = 4;
static const int PROMO_TYPE_DISCOUNT = 5;
static const int PROMO_TYPE_FREE = 6;
static const int PROMO_TYPE_SPECIAL_PRICE_MEMBER = 7;
static const int PROMO_TYPE_DISCOUNT_MEMBER = 8;
static const int PROMO_TYPE_FREE_MEMBER = 9;
static const int PROMO_TYPE_SPECIAL_PRICE_VOUCHER = 11;
static const int PROMO_TYPE_DISCOUNT_VOUCHER = 12;
static const int PROMO_TYPE_FREE_VOUCHER = 13;
static const int PROMO_TYPE_DEALS = 15;
static const String TAX_CATEGORY_DISC = "disc";
static const String TAX_CATEGORY_VOUCHER = "voucher";
}
extension IntExtension on int? {
String toCurrency({String prefix = ''}) {
var num = NumberFormat.decimalPattern();
try {
return prefix + num.format(this ?? 0);
} catch (e) {
return '${prefix}0';
}
}
String dateTimeFormat([String? format]) {
final DateFormat sdf = DateFormat(format ?? 'dd-MM-yyyy HH:mm');
return sdf.format(DateTime.fromMillisecondsSinceEpoch(this ?? 0));
}
}
extension StringExtensions on String {
String safeRepeat(int? count) {
return count != null && count > 0 ? this * count : '';
}
String loop(int w) {
var newVal = '';
for (var i = 0; i < w; i++) {
newVal += this;
}
return newVal;
}
String width(int width, {int max = 32, int before = 0}) {
try {
var cursor = 0;
var wordSplit = StringBuffer();
do {
var suffix = '';
var start = cursor;
var end = (start + width) < length ? start + width : length;
var cutWord = substring(start, end);
var lastSpace = cutWord.contains(' ') && end != length
? cutWord.lastIndexOf(' ')
: cutWord.length;
if (lastSpace < (width ~/ 2) && (width - 1) < cutWord.length) {
lastSpace = width - 1;
cursor--;
suffix = '-';
}
cursor += lastSpace + 1;
suffix += cursor < length
? ' '.padRight(max - (before + lastSpace)) + '\n'
: ' '.padRight(width - (before + lastSpace));
wordSplit.write(cutWord.substring(0, lastSpace) + suffix);
} while (cursor < length);
return wordSplit.toString();
} catch (e) {
// Handle error
}
// Fallback logic
return padRight(width);
}
String widthRight(int w) {
return padLeft(w);
}
String center(int width, [String flanks = ' ']) {
if (length == width) {
return this;
} else if (length < width) {
var center = width ~/ 2;
var flank = center - (length ~/ 2);
var newValue = StringBuffer();
for (var i = 0; i < flank; i++) {
newValue.write(flanks);
}
newValue.write(this);
for (var i = newValue.length; i < width; i++) {
newValue.write(flanks);
}
return newValue.toString();
} else {
// Handle case where length > width
}
return '';
}
}
SalesEntity dummySales() {
return SalesEntity(
displayNota: '1001',
customer: 'John Doe',
timeCreated: DateTime.now().millisecondsSinceEpoch,
payment: 'CASH',
table: 'Table 5',
payments: [
Payment(
method: 'CARD',
pay: 5000,
total: 5000,
bank: Bank(name: 'BCA', bankId: 1),
),
],
orderList: [
Order(
isItemVoid: false,
product: Product(
productId: 1,
name: 'Ayam Goreng',
productSubcategoryFkid: 1,
productDetailId: 1),
qty: 2,
subTotal: 20000,
extra: [],
promotion: Promotion(
name: "Promo Coblosan",
promotionValue: 500,
pomotionTypeFkid: 5,
),
discount: Discount(
discount: 5,
voucher: 0,
discountNominal: 2500,
voucherNominal: 0,
discountInfo: 'Flash Deals',
discountType: Constant.TYPE_NOMINAL),
),
Order(
isItemVoid: false,
product: Product(
productId: 2,
name: 'Es Teh',
productDetailId: 2,
productSubcategoryFkid: 2),
qty: 1,
subTotal: 2000,
extra: [],
promotion: null,
discount: Discount(
discount: 0,
voucher: 0,
discountNominal: 0,
voucherNominal: 0,
discountType: Constant.TYPE_NOMINAL),
),
],
promotions: [],
discount: Discount(
discount: 5,
voucher: 0,
discountNominal: 1000,
voucherNominal: 0,
discountInfo: 'Diskon Karyawan',
discountType: Constant.TYPE_PERCENT),
taxes: [
Tax(name: 'Tax Pb1', total: 50, category: "tax"),
Tax(name: 'Service', total: 10, category: "service"),
],
grandTotal: 5000,
status: 'Success');
}
Outlet dummyOutlet() {
return Outlet(
name: 'Outlet ABC',
receiptAddress: '123 Main Street',
receiptPhone: '123-456-7890',
receiptNote: 'Thank you for your visit!',
receiptSocialmedia: '@OutletABC',
);
}
List<CashRecapEntity> generateDummyCashRecap() {
return [
CashRecapEntity(
id: 1,
timeCreated: DateTime.now()
.millisecondsSinceEpoch, // Dummy timeCreated using current timestamp
employeeName: "John Doe",
pendingBillTotal: 100,
cash: 500,
card: 300,
),
CashRecapEntity(
id: 2,
timeCreated: DateTime.now()
.millisecondsSinceEpoch, // Dummy timeCreated using current timestamp
employeeName: "Jane Smith",
pendingBillTotal: 50,
cash: 300,
card: 200,
),
];
}
List<TmpSalesEntity> generateDummyTmpSales(int count) {
List<TmpSalesEntity> tmpSalesList = [];
for (int i = 0; i < count; i++) {
tmpSalesList.add(
TmpSalesEntity(
id: i + 1,
sales:
'Sales data ${Random().nextInt(100)}', // Generate random sales data
),
);
}
return tmpSalesList;
}
// Function to generate dummy data for OperationalCost
List<OperationalCost> generateDummyOperationalCost(int count) {
List<OperationalCost> operationalCostList = [];
for (int i = 0; i < count; i++) {
operationalCostList.add(
OperationalCost(
id: i + 1,
opcostName:
'Operational Cost ${Random().nextInt(100)}', // Generate random opcost name
total: Random().nextInt(10000), // Generate random total
),
);
}
return operationalCostList;
}
// Function to generate dummy data for SubCategoryEntity
List<SubCategoryEntity> generateDummySubCategories(int count) {
List<SubCategoryEntity> subCategoryList = [];
for (int i = 0; i < count; i++) {
subCategoryList.add(
SubCategoryEntity(
id: i + 1,
name:
'Subcategory ${Random().nextInt(100)}', // Generate random subcategory name
productCategoryId: i +
1, // Generate random productCategoryId (assuming there are 10 categories)
),
);
}
return subCategoryList;
}
// SalesEntity model
class SalesEntity {
String displayNota;
String customer;
int timeCreated;
String payment;
String table;
List<Payment> payments;
List<Order> orderList;
List<Promotion> promotions;
Discount? discount;
List<Tax>? taxes;
int grandTotal;
String status;
SalesTagEntity? salesTag;
int customersQty;
SalesEntity(
{required this.displayNota,
required this.customer,
required this.timeCreated,
required this.payment,
required this.table,
required this.payments,
required this.orderList,
required this.promotions,
this.discount,
this.taxes,
required this.grandTotal,
required this.status,
this.salesTag,
this.customersQty = 1});
factory SalesEntity.fromJson(Map<String, dynamic> json) {
return SalesEntity(
displayNota: json['displayNota'] ?? '',
customer: json['customer'] ?? '',
timeCreated: json['timeCreated'] ?? 0,
payment: json['payment'] ?? '',
table: json['table'] ?? '',
payments: (json['payments'] ?? [])
.map<Payment>((paymentJson) => Payment.fromJson(paymentJson))
.toList(),
orderList: (json['orderList'] ?? [])
.map<Order>((orderJson) => Order.fromJson(orderJson))
.toList(),
promotions: (json['promotions'] ?? [])
.map<Promotion>((promotionJson) => Promotion.fromJson(promotionJson))
.toList(),
discount:
json['discount'] != null ? Discount.fromJson(json['discount']) : null,
taxes: (json['taxes'] ?? [])
.map<Tax>((taxJson) => Tax.fromJson(taxJson))
.toList(),
grandTotal: json['grandTotal'] ?? 0,
status: json['status'] ?? '',
salesTag: json['salesTag'] != null
? SalesTagEntity.fromJson(json['salesTag'])
: null,
);
}
}
class SalesTagEntity {
final int salesTagId;
final int adminFkid;
final String dataStatus;
final int dateCreated;
final int dateModified;
final String name;
SalesTagEntity({
required this.salesTagId,
required this.adminFkid,
required this.dataStatus,
required this.dateCreated,
required this.dateModified,
required this.name,
});
factory SalesTagEntity.fromJson(Map<String, dynamic> json) {
return SalesTagEntity(
salesTagId: json['salesTagId'],
adminFkid: json['adminFkid'],
dataStatus: json['dataStatus'],
dateCreated: json['dateCreated'],
dateModified: json['dateModified'],
name: json['name'],
);
}
}
// Outlet model
class Outlet {
String name;
String? receiptAddress;
String? receiptPhone;
String? receiptNote;
String? receiptSocialmedia;
Outlet({
required this.name,
this.receiptAddress,
this.receiptPhone,
this.receiptNote,
this.receiptSocialmedia,
});
}
// Employee model
class Employee {
String name;
Employee({
required this.name,
});
}
// Payment model
class Payment {
String method;
int pay;
Bank? bank;
int total;
Payment({
required this.method,
required this.pay,
this.bank,
required this.total,
});
factory Payment.fromJson(Map<String, dynamic> json) {
return Payment(
method: json['method'] ?? '',
pay: json['pay'] ?? 0,
bank: json['bank'] != null
? Bank.fromJson(json['bank'])
: null, // Assuming Bank has a fromJson method
total: json['total'] ?? 0,
);
}
}
// Order model
class Order {
String? note;
bool isItemVoid;
Product product;
int qty;
int subTotal;
List<Order> extra;
Promotion? promotion;
Discount discount;
String? extraType;
Order(
{this.note,
required this.isItemVoid,
required this.product,
required this.qty,
required this.subTotal,
required this.extra,
this.promotion,
required this.discount,
this.extraType});
factory Order.fromJson(Map<String, dynamic> json) {
return Order(
note: json['note'],
isItemVoid: json['isItemVoid'] ?? false,
product: Product.fromJson(json['product']),
qty: json['qty'] ?? 0,
subTotal: json['subTotal'] ?? 0,
extra: (json['extra'] as List<dynamic>?)
?.map((e) => Order.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
promotion: json['promotion'] != null
? Promotion.fromJson(json['promotion'] as Map<String, dynamic>)
: null,
discount: Discount.fromJson(json['discount']),
extraType: json['extraType'],
);
}
}
// Product model
class Product {
int productId;
String? name;
int productSubcategoryFkid;
int productDetailId;
Product({
required this.productId,
this.name,
required this.productSubcategoryFkid,
required this.productDetailId,
});
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
productId: json['productId'],
name: json['name'],
productSubcategoryFkid: json['productSubcategoryFkid'],
productDetailId: json['productDetailId'],
);
}
}
// Promotion model
class Promotion {
String? name;
String? displayVoucher;
int promotionValue;
int? pomotionTypeFkid;
Promotion({
this.name,
this.displayVoucher,
required this.promotionValue,
this.pomotionTypeFkid,
});
factory Promotion.fromJson(Map<String, dynamic> json) {
return Promotion(
name: json['name'],
displayVoucher: json['displayVoucher'],
promotionValue: json['promotionValue'],
pomotionTypeFkid: json['pomotionTypeFkid'],
);
}
}
// Discount model
class Discount {
int discount;
int voucher;
int discountNominal;
int voucherNominal;
String discountType;
String? discountInfo;
String? voucherInfo;
Discount({
required this.discount,
required this.voucher,
required this.discountNominal,
required this.voucherNominal,
required this.discountType,
this.discountInfo,
this.voucherInfo,
});
factory Discount.fromJson(Map<String, dynamic> json) {
return Discount(
discount: json['discount'],
voucher: json['voucher'],
discountNominal: json['discountNominal'],
voucherNominal: json['voucherNominal'],
discountType: json['discountType'],
discountInfo: json['discountInfo'],
voucherInfo: json['voucherInfo'],
);
}
}
// Tax model
class Tax {
String? name;
int total;
String category;
Tax({
this.name,
required this.total,
required this.category,
});
factory Tax.fromJson(Map<String, dynamic> json) {
return Tax(
name: json['name'],
total: json['total'],
category: json['category'],
);
}
}
// Bank model
class Bank {
String name;
int bankId;
Bank({
required this.name,
required this.bankId,
});
factory Bank.fromJson(Map<String, dynamic> json) {
return Bank(
name: json['name'] ?? '',
bankId: json['bankId'] ?? 0,
);
}
}
class CashRecapEntity {
int id;
int timeCreated;
String employeeName;
int pendingBillTotal;
int cash;
int card;
CashRecapEntity({
required this.id,
required this.timeCreated,
required this.employeeName,
required this.pendingBillTotal,
required this.cash,
required this.card,
});
factory CashRecapEntity.fromJson(Map<String, dynamic> json) {
return CashRecapEntity(
id: json['id'] as int,
timeCreated: json['time_created'] as int,
employeeName: json['employee_name'] as String,
pendingBillTotal: json['pending_bill_total'] as int,
cash: json['cash'] as int,
card: json['card'] as int,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'time_created': timeCreated,
'employee_name': employeeName,
'pending_bill_total': pendingBillTotal,
'cash': cash,
'card': card,
};
}
}
class TmpSalesEntity {
int id;
String sales; // Assuming this is a JSON string representing sales data
TmpSalesEntity({
required this.id,
required this.sales,
});
factory TmpSalesEntity.fromJson(Map<String, dynamic> json) {
return TmpSalesEntity(
id: json['id'] as int,
sales: json['sales'] as String,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'sales': sales,
};
}
}
class OperationalCost {
int id;
String opcostName;
int total;
OperationalCost({
required this.id,
required this.opcostName,
required this.total,
});
factory OperationalCost.fromJson(Map<String, dynamic> json) {
return OperationalCost(
id: json['id'] as int,
opcostName: json['opcostName'] as String,
total: (json['total'] as num).toInt(),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'opcostName': opcostName,
'total': total,
};
}
}
class SubCategoryEntity {
int id;
String name;
int productCategoryId;
SubCategoryEntity({
required this.id,
required this.name,
required this.productCategoryId,
});
factory SubCategoryEntity.fromJson(Map<String, dynamic> json) {
return SubCategoryEntity(
id: json['id'] as int,
name: json['name'] as String,
productCategoryId: json['productCategoryId'] as int,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'productCategoryId': productCategoryId,
};
}
}
class OpenShiftEntity {
int id;
double earlyCash;
OpenShiftEntity({
required this.id,
required this.earlyCash,
});
factory OpenShiftEntity.fromJson(Map<String, dynamic> json) {
return OpenShiftEntity(
id: json['id'] as int,
earlyCash: json['earlyCash'] as double,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'earlyCash': earlyCash,
};
}
}
class RolePrinterClosing {
final bool sales;
final bool paymentmedia;
final bool groupsales;
final bool refund;
final bool time;
final bool tax;
final bool itemsales;
final bool avgpaxbill;
final bool fifo;
final bool actual;
final bool opcostdetail;
final bool entertainincome;
RolePrinterClosing({
this.sales = false,
this.paymentmedia = false,
this.groupsales = false,
this.refund = false,
this.time = false,
this.tax = false,
this.itemsales = false,
this.avgpaxbill = false,
this.fifo = false,
this.actual = false,
this.opcostdetail = false,
this.entertainincome = false,
});
factory RolePrinterClosing.fromJson(Map<String, dynamic> json) {
return RolePrinterClosing(
sales: json['sales'] ?? false,
paymentmedia: json['paymentmedia'] ?? false,
groupsales: json['groupsales'] ?? false,
refund: json['refund'] ?? false,
time: json['time'] ?? false,
tax: json['tax'] ?? false,
itemsales: json['itemsales'] ?? false,
avgpaxbill: json['avgpaxbill'] ?? false,
fifo: json['fifo'] ?? false,
actual: json['actual'] ?? false,
opcostdetail: json['opcostdetail'] ?? false,
entertainincome: json['entertainincome'] ?? false,
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment