Include Due Date, Taxable, and Tax Rate columns in Invoice Item Report #273

This commit is contained in:
Hillel Coren 2021-05-23 17:15:47 +03:00
parent 92204b13e2
commit 2b2f6b701a
8 changed files with 114 additions and 24 deletions

View File

@ -1210,30 +1210,42 @@ abstract class InvoiceItemEntity
@nullable @nullable
int get createdAt; int get createdAt;
/* double taxAmount(InvoiceEntity invoice, int precision) {
double taxAmount(bool useInclusiveTaxes, int precision) {
double calculateTaxAmount(double rate) { double calculateTaxAmount(double rate) {
double taxAmount; double taxAmount;
if (rate == 0) { if (rate == 0) {
return 0; return 0;
} }
if (useInclusiveTaxes) { final lineTotal = total(invoice);
taxAmount = total - (total / (1 + (rate / 100))); if (invoice.usesInclusiveTaxes) {
taxAmount = lineTotal - (lineTotal / (1 + (rate / 100)));
} else { } else {
taxAmount = total * rate / 100; taxAmount = lineTotal * rate / 100;
} }
return round(taxAmount, precision); return round(taxAmount, precision);
} }
return calculateTaxAmount(taxRate1) + calculateTaxAmount(taxRate2) + return calculateTaxAmount(taxRate1) +
calculateTaxAmount(taxRate2) +
calculateTaxAmount(taxRate3); calculateTaxAmount(taxRate3);
} }
double netTotal(bool useInclusiveTaxes, int precision) => double netTotal(InvoiceEntity invoice, int precision) =>
total - taxAmount(useInclusiveTaxes, precision); total(invoice) - taxAmount(invoice, precision);
*/
double get total => round(quantity * cost, 2); double total(InvoiceEntity invoice) {
var total = quantity * cost;
if (discount != 0) {
if (invoice.isAmountDiscount) {
total = total - discount;
} else {
total = total - (discount / 100 * total);
}
}
return round(total, 2);
}
bool get isTask => typeId == TYPE_TASK; bool get isTask => typeId == TYPE_TASK;
@ -1245,9 +1257,31 @@ abstract class InvoiceItemEntity
cost == 0 && cost == 0 &&
quantity == 0 && quantity == 0 &&
customValue1.isEmpty && customValue1.isEmpty &&
customValue2.isEmpty; customValue2.isEmpty &&
customValue3.isEmpty &&
customValue4.isEmpty;
// TODO add custom 3 and 4 bool get hasTaxes =>
taxRate1 != 0 ||
taxRate2 != 0 ||
taxRate3 != 0 ||
taxName1.isNotEmpty ||
taxName2.isNotEmpty ||
taxName3.isNotEmpty;
String get taxRates {
final parts = <String>[];
if (taxName1.isNotEmpty) {
parts.add(taxName1);
}
if (taxName2.isNotEmpty) {
parts.add(taxName2);
}
if (taxName3.isNotEmpty) {
parts.add(taxName3);
}
return parts.join(', ');
}
InvoiceItemEntity applyTax(TaxRateEntity taxRate, InvoiceItemEntity applyTax(TaxRateEntity taxRate,
{bool isSecond = false, bool isThird = false}) { {bool isSecond = false, bool isThird = false}) {

View File

@ -93,7 +93,7 @@ class InvoiceItemListTile extends StatelessWidget {
title: Row( title: Row(
children: <Widget>[ children: <Widget>[
Expanded(child: Text(invoiceItem.productKey)), Expanded(child: Text(invoiceItem.productKey)),
Text(formatNumber(invoiceItem.total, context, Text(formatNumber(invoiceItem.total(invoice), context,
clientId: invoice.clientId)), clientId: invoice.clientId)),
], ],
), ),

View File

@ -133,6 +133,7 @@ class EntityPresenter {
'calculated_rate', 'calculated_rate',
'duration', 'duration',
'net_amount', 'net_amount',
'net_total',
].contains(field); ].contains(field);
return value; return value;

View File

@ -576,7 +576,7 @@ class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
readOnly: true, readOnly: true,
enabled: false, enabled: false,
initialValue: formatNumber( initialValue: formatNumber(
lineItems[index].total, context, lineItems[index].total(invoice), context,
clientId: invoice.clientId), clientId: invoice.clientId),
textAlign: TextAlign.right, textAlign: TextAlign.right,
), ),

View File

@ -17,7 +17,7 @@ enum InvoiceItemReportFields {
cost, cost,
quantity, quantity,
profit, profit,
lineTotal, total,
discount, discount,
custom1, custom1,
custom2, custom2,
@ -26,6 +26,11 @@ enum InvoiceItemReportFields {
invoiceNumber, invoiceNumber,
invoiceDate, invoiceDate,
client, client,
dueDate,
hasTaxes,
taxRates,
taxAmount,
netTotal,
} }
var memoizedInvoiceItemReport = memo6(( var memoizedInvoiceItemReport = memo6((
@ -81,6 +86,7 @@ ReportResult lineItemReport(
for (var entry in invoiceMap.entries) { for (var entry in invoiceMap.entries) {
final invoice = entry.value; final invoice = entry.value;
final client = clientMap[invoice.clientId]; final client = clientMap[invoice.clientId];
final precision = staticState.currencyMap[client.currencyId].precision;
if (invoice.isDeleted || client.isDeleted) { if (invoice.isDeleted || client.isDeleted) {
continue; continue;
@ -105,9 +111,8 @@ ReportResult lineItemReport(
value = productId == null ? 0.0 : productMap[productId].cost; value = productId == null ? 0.0 : productMap[productId].cost;
break; break;
case InvoiceItemReportFields.profit: case InvoiceItemReportFields.profit:
value = productId == null value = lineItem.netTotal(invoice, precision) -
? 0.0 (productId == null ? 0.0 : productMap[productId].cost);
: lineItem.total - productMap[productId].cost;
break; break;
case InvoiceItemReportFields.custom1: case InvoiceItemReportFields.custom1:
value = lineItem.customValue1; value = lineItem.customValue1;
@ -124,7 +129,7 @@ ReportResult lineItemReport(
case InvoiceItemReportFields.notes: case InvoiceItemReportFields.notes:
value = lineItem.notes; value = lineItem.notes;
break; break;
case InvoiceItemReportFields.lineTotal: case InvoiceItemReportFields.total:
value = lineItem.total; value = lineItem.total;
break; break;
case InvoiceItemReportFields.productKey: case InvoiceItemReportFields.productKey:
@ -142,6 +147,21 @@ ReportResult lineItemReport(
case InvoiceItemReportFields.client: case InvoiceItemReportFields.client:
value = client.displayName; value = client.displayName;
break; break;
case InvoiceItemReportFields.dueDate:
value = invoice.dueDate;
break;
case InvoiceItemReportFields.hasTaxes:
value = lineItem.hasTaxes;
break;
case InvoiceItemReportFields.taxRates:
value = lineItem.taxRates;
break;
case InvoiceItemReportFields.taxAmount:
value = lineItem.taxAmount(invoice, precision);
break;
case InvoiceItemReportFields.netTotal:
value = lineItem.netTotal(invoice, precision);
break;
} }
if (!ReportResult.matchField( if (!ReportResult.matchField(

View File

@ -17,7 +17,7 @@ enum QuoteItemReportFields {
cost, cost,
quantity, quantity,
profit, profit,
lineTotal, total,
discount, discount,
custom1, custom1,
custom2, custom2,
@ -26,6 +26,11 @@ enum QuoteItemReportFields {
quoteNumber, quoteNumber,
quoteDate, quoteDate,
client, client,
validUntil,
hasTaxes,
taxRates,
taxAmount,
netTotal,
} }
var memoizedQuoteItemReport = memo6(( var memoizedQuoteItemReport = memo6((
@ -81,6 +86,7 @@ ReportResult lineItemReport(
for (var entry in invoiceMap.entries) { for (var entry in invoiceMap.entries) {
final invoice = entry.value; final invoice = entry.value;
final client = clientMap[invoice.clientId]; final client = clientMap[invoice.clientId];
final precision = staticState.currencyMap[client.currencyId].precision;
if (invoice.isDeleted || client.isDeleted) { if (invoice.isDeleted || client.isDeleted) {
continue; continue;
@ -105,9 +111,8 @@ ReportResult lineItemReport(
value = productId == null ? 0.0 : productMap[productId].cost; value = productId == null ? 0.0 : productMap[productId].cost;
break; break;
case QuoteItemReportFields.profit: case QuoteItemReportFields.profit:
value = productId == null value = lineItem.netTotal(invoice, precision) -
? 0.0 (productId == null ? 0.0 : productMap[productId].cost);
: lineItem.total - productMap[productId].cost;
break; break;
case QuoteItemReportFields.custom1: case QuoteItemReportFields.custom1:
value = lineItem.customValue1; value = lineItem.customValue1;
@ -124,7 +129,7 @@ ReportResult lineItemReport(
case QuoteItemReportFields.notes: case QuoteItemReportFields.notes:
value = lineItem.notes; value = lineItem.notes;
break; break;
case QuoteItemReportFields.lineTotal: case QuoteItemReportFields.total:
value = lineItem.total; value = lineItem.total;
break; break;
case QuoteItemReportFields.productKey: case QuoteItemReportFields.productKey:
@ -142,6 +147,21 @@ ReportResult lineItemReport(
case QuoteItemReportFields.client: case QuoteItemReportFields.client:
value = client.displayName; value = client.displayName;
break; break;
case QuoteItemReportFields.validUntil:
value = invoice.dueDate;
break;
case QuoteItemReportFields.hasTaxes:
value = lineItem.hasTaxes;
break;
case QuoteItemReportFields.taxRates:
value = lineItem.taxRates;
break;
case QuoteItemReportFields.taxAmount:
value = lineItem.taxAmount(invoice, precision);
break;
case QuoteItemReportFields.netTotal:
value = lineItem.netTotal(invoice, precision);
break;
} }
if (!ReportResult.matchField( if (!ReportResult.matchField(

View File

@ -534,6 +534,8 @@ enum ReportColumnType {
} }
ReportColumnType getReportColumnType(String column, BuildContext context) { ReportColumnType getReportColumnType(String column, BuildContext context) {
column = toSnakeCase(column);
ReportColumnType convertCustomFieldType(String type) { ReportColumnType convertCustomFieldType(String type) {
if (type == kFieldTypeDate) { if (type == kFieldTypeDate) {
return ReportColumnType.date; return ReportColumnType.date;
@ -1125,6 +1127,9 @@ class ReportResult {
final sortedColumns = columns.toList() final sortedColumns = columns.toList()
..sort((String str1, String str2) => str1.compareTo(str2)); ..sort((String str1, String str2) => str1.compareTo(str2));
for (String column in sortedColumns)
print('## $column => ${getReportColumnType(column, context)}');
final totalColumns = [ final totalColumns = [
DataColumn( DataColumn(
label: Text(localization.currency), label: Text(localization.currency),

View File

@ -15,6 +15,8 @@ mixin LocalizationsProvider on LocaleCodeAware {
static final Map<String, Map<String, String>> _localizedValues = { static final Map<String, Map<String, String>> _localizedValues = {
'en': { 'en': {
// STARTER: lang key - do not remove comment // STARTER: lang key - do not remove comment
'net_total': 'Net Total',
'has_taxes': 'Has Taxes',
'import_customers': 'Import Customers', 'import_customers': 'Import Customers',
'imported_customers': 'Successfully started importing customers', 'imported_customers': 'Successfully started importing customers',
'login_success': 'Successful Login', 'login_success': 'Successful Login',
@ -60405,6 +60407,14 @@ mixin LocalizationsProvider on LocaleCodeAware {
_localizedValues[localeCode]['imported_customers'] ?? _localizedValues[localeCode]['imported_customers'] ??
_localizedValues['en']['imported_customers']; _localizedValues['en']['imported_customers'];
String get hasTaxes =>
_localizedValues[localeCode]['has_taxes'] ??
_localizedValues['en']['has_taxes'];
String get netTotal =>
_localizedValues[localeCode]['net_total'] ??
_localizedValues['en']['net_total'];
String lookup(String key) { String lookup(String key) {
final lookupKey = toSnakeCase(key); final lookupKey = toSnakeCase(key);