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
int get createdAt;
/*
double taxAmount(bool useInclusiveTaxes, int precision) {
double taxAmount(InvoiceEntity invoice, int precision) {
double calculateTaxAmount(double rate) {
double taxAmount;
if (rate == 0) {
return 0;
}
if (useInclusiveTaxes) {
taxAmount = total - (total / (1 + (rate / 100)));
final lineTotal = total(invoice);
if (invoice.usesInclusiveTaxes) {
taxAmount = lineTotal - (lineTotal / (1 + (rate / 100)));
} else {
taxAmount = total * rate / 100;
taxAmount = lineTotal * rate / 100;
}
return round(taxAmount, precision);
}
return calculateTaxAmount(taxRate1) + calculateTaxAmount(taxRate2) +
return calculateTaxAmount(taxRate1) +
calculateTaxAmount(taxRate2) +
calculateTaxAmount(taxRate3);
}
double netTotal(bool useInclusiveTaxes, int precision) =>
total - taxAmount(useInclusiveTaxes, precision);
*/
double netTotal(InvoiceEntity invoice, int 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;
@ -1245,9 +1257,31 @@ abstract class InvoiceItemEntity
cost == 0 &&
quantity == 0 &&
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,
{bool isSecond = false, bool isThird = false}) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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