Skip to content

Commit

Permalink
Keeps initial product quantity unchanged
Browse files Browse the repository at this point in the history
Previously, the product quantity was being decreased as new sales was being created. But the generated product report required to display the original product quantity.

After this changes, the original product quantity will remain untouched. A new variable will be introduced to track number of units sold.

Available units will be determined on the fly.

While editing product, the price and quantity can not be modified after more than 1 units has been sold.
  • Loading branch information
dipu-bd committed Jun 10, 2021
1 parent 799cb72 commit 19738a5
Show file tree
Hide file tree
Showing 17 changed files with 167 additions and 127 deletions.
4 changes: 2 additions & 2 deletions lib/src/blocs/repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ class Repository {
final doc = _products.doc(sales.productId);
return doc.get().then((snapshot) {
final product = snapshot.data()!;
assert(product.quantity >= sales.quantity);
doc.update({'quantity': product.quantity - sales.quantity});
assert(product.availableUnits >= sales.quantity);
doc.update({'units_sold': product.unitsSold + sales.quantity});
return _sales.add(sales);
});
}
Expand Down
20 changes: 17 additions & 3 deletions lib/src/models/product.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ export 'package:sales_tracker/src/models/item.dart';
class Product extends Item {
late final String name;
late final double unitCost;
late final int unitsSold;

double get cost => quantity * unitCost;
double get totalCost => quantity * unitCost;

int get availableUnits => quantity - unitsSold;

double get availableCost => availableUnits * unitCost;

Product({
required this.name,
required this.unitCost,
required int quantity,
required DateTime date,
}) : super(
}) : unitsSold = 0,
super(
date: date,
quantity: quantity,
);
Expand All @@ -23,18 +29,26 @@ class Product extends Item {
: super.fromJson(id, data) {
name = data.remove('name');
unitCost = data.remove('unit_price');
unitsSold = data.remove('units_sold') ?? 0;
}

Map<String, dynamic> toJson() {
final data = super.toJson();
data['name'] = name;
data['unit_price'] = unitCost;
data['units_sold'] = unitsSold;
return data;
}
}

extension ProductFormat on Product {
String get unitsSoldStr => unitsSold.toString();

String get availableUnitsStr => availableUnits.toString();

String get unitCostStr => formatCurrency(unitCost);

String get costStr => formatCurrency(cost);
String get totalCostStr => formatCurrency(totalCost);

String get availableCostStr => formatCurrency(availableCost);
}
8 changes: 7 additions & 1 deletion lib/src/models/product_report.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import 'package:sales_tracker/src/utils/formatters.dart';
export 'package:sales_tracker/src/models/report.dart';

class ProductReport extends Report<Product> {
late final int totalSold;
late final double totalCost;

int get remainingUnits => totalUnits - totalSold;

ProductReport({
required DateTime startTime,
required DateTime endTime,
Expand All @@ -16,11 +19,14 @@ class ProductReport extends Report<Product> {
endTime: endTime,
items: products,
) {
int sold = 0;
double cost = 0;
for (final item in products) {
cost += item.cost;
sold += item.unitsSold;
cost += item.totalCost;
}
totalCost = cost;
totalSold = sold;
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/src/models/report.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Report<T extends Item> {
final DateTime endTime;
final List<T> items;
final Map<DateTime, List<T>> groups = {};
late final int totalItems;
late final int totalUnits;

Report({
required this.items,
Expand All @@ -28,7 +28,7 @@ class Report<T extends Item> {
groups.putIfAbsent(group, () => []);
groups[group]!.add(item);
}
totalItems = total;
totalUnits = total;
}
}

Expand Down
32 changes: 16 additions & 16 deletions lib/src/models/sales_record.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,26 @@ export 'package:sales_tracker/src/models/item.dart';
class SalesRecord extends Item {
late final String productId;
late final String productName;
late final double unitBuyPrice;
late final double unitSellPrice;
late final double unitCost;
late final double unitPrice;

double get buyingPrice => quantity * unitBuyPrice;
double get totalCost => quantity * unitCost;

double get sellingPrice => quantity * unitSellPrice;
double get totalPrice => quantity * unitPrice;

double get profit => sellingPrice - buyingPrice;
double get profit => totalPrice - totalCost;

double get profitPerUnit => (sellingPrice - buyingPrice) / quantity;
double get profitPerUnit => (totalPrice - totalCost) / quantity;

SalesRecord({
required Product product,
required this.unitSellPrice,
required this.unitPrice,
required int quantity,
required DateTime date,
}) : assert(product.id != null),
productId = product.id!,
productName = product.name,
unitBuyPrice = product.unitCost,
unitCost = product.unitCost,
super(
date: date,
quantity: quantity,
Expand All @@ -36,28 +36,28 @@ class SalesRecord extends Item {
: super.fromJson(id, data) {
productId = data.remove('product_id');
productName = data.remove('product_name');
unitBuyPrice = data.remove('buy_price');
unitSellPrice = data.remove('sell_price');
unitCost = data.remove('buy_price') ?? 0;
unitPrice = data.remove('sell_price') ?? 0;
}

Map<String, dynamic> toJson() {
final data = super.toJson();
data['product_id'] = productId;
data['product_name'] = productName;
data['buy_price'] = unitBuyPrice;
data['sell_price'] = unitSellPrice;
data['buy_price'] = unitCost;
data['sell_price'] = unitPrice;
return data;
}
}

extension SalesRecordFormat on SalesRecord {
String get unitBuyPriceStr => formatCurrency(unitBuyPrice);
String get unitCostStr => formatCurrency(unitCost);

String get unitSellPriceStr => formatCurrency(unitSellPrice);
String get unitPriceStr => formatCurrency(unitPrice);

String get buyingPriceStr => formatCurrency(buyingPrice);
String get totalCostStr => formatCurrency(totalCost);

String get sellingPriceStr => formatCurrency(sellingPrice);
String get totalPriceStr => formatCurrency(totalPrice);

String get profitStr => formatCurrency(profit);

Expand Down
4 changes: 2 additions & 2 deletions lib/src/models/sales_report.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ class SalesReport extends Report<SalesRecord> {
double cost = 0;
double price = 0;
for (final record in sales) {
cost += record.buyingPrice;
price += record.sellingPrice;
cost += record.totalCost;
price += record.totalPrice;
}
totalCost = cost;
totalPrice = price;
Expand Down
18 changes: 11 additions & 7 deletions lib/src/pages/widgets/home_page_drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ class HomePageDrawer extends StatelessWidget {

Widget buildUserName() {
final user = FirebaseAuth.instance.currentUser;
if (user?.displayName == null) return Container();
if (user?.displayName == null) {
return Container();
}
return Text(
user!.displayName!,
textAlign: TextAlign.center,
Expand Down Expand Up @@ -133,7 +135,7 @@ class HomePageDrawer extends StatelessWidget {
),
onTap: () async {
await SalesReportPage.display(context);
Navigator.of(context).pop();
Navigator.of(context, rootNavigator: true).pop();
},
);
}
Expand All @@ -146,9 +148,9 @@ class HomePageDrawer extends StatelessWidget {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
onTap: () {
Navigator.of(context).pop();
ProductFormDialog.display(context);
onTap: () async {
await ProductFormDialog.display(context);
Navigator.of(context, rootNavigator: true).pop();
},
);
}
Expand All @@ -173,13 +175,15 @@ class HomePageDrawer extends StatelessWidget {
TextButton(
child: Text('Continue'),
onPressed: () {
Navigator.of(context).pop();
Repository.of(context).clearAllData();
Navigator.of(context).pop();
},
),
TextButton(
child: Text('Cancel'),
onPressed: () => Navigator.of(context).pop(),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
},
),
],
),
Expand Down
10 changes: 6 additions & 4 deletions lib/src/pages/widgets/product_form_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import 'package:sales_tracker/src/models/product.dart';
import 'package:sales_tracker/src/pages/widgets/error_message.dart';

class ProductFormDialog extends StatelessWidget {
static void display(BuildContext context, [Product? product]) {
Navigator.of(context).push(
static Future<void> display(BuildContext context, [Product? product]) {
return Navigator.of(context, rootNavigator: true).push(
MaterialPageRoute(
maintainState: false,
fullscreenDialog: true,
Expand All @@ -31,7 +31,7 @@ class ProductFormDialog extends StatelessWidget {
unitPriceInput = TextEditingController(text: product?.unitCost.toString());
quantityInput = TextEditingController(text: product?.quantity.toString());
dateInput = TextEditingController(
text: product?.date != null ? _dateFormatter.format(product!.date) : '',
text: _dateFormatter.format(product?.date ?? DateTime.now()),
);
}

Expand Down Expand Up @@ -119,11 +119,12 @@ class ProductFormDialog extends StatelessWidget {

Widget buildUnitPriceInput(BuildContext context) {
return TextField(
enabled: product == null || product!.unitsSold == 0,
controller: unitPriceInput,
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
labelText: 'Cost Per Unit',
labelText: 'Cost Per Unit',
),
onEditingComplete: () {
FocusScope.of(context).nextFocus();
Expand All @@ -142,6 +143,7 @@ class ProductFormDialog extends StatelessWidget {

Widget buildQuantityInput(BuildContext context) {
return TextField(
enabled: product == null || product!.unitsSold == 0,
controller: quantityInput,
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
Expand Down
23 changes: 15 additions & 8 deletions lib/src/pages/widgets/product_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class ProductItemTile extends StatelessWidget {
buildDeleteAction(context),
buildEditAction(context),
],
secondaryActions: product.quantity > 0
secondaryActions: product.availableUnits > 0
? <Widget>[
buildSaleAction(context),
]
Expand Down Expand Up @@ -71,7 +71,7 @@ class ProductItemTile extends StatelessWidget {
return CircleAvatar(
backgroundColor: Colors.grey.shade300,
child: Text(
'${product.quantity > 99 ? '99+' : product.quantity}',
'${product.availableUnits > 99 ? '99+' : product.availableUnits}',
style: TextStyle(
fontFamily: 'monospace',
fontWeight: FontWeight.bold,
Expand All @@ -98,21 +98,28 @@ class ProductItemTile extends StatelessWidget {

Widget buildPrice() {
return Container(
constraints: BoxConstraints(
minWidth: 75,
),
// constraints: BoxConstraints(
// minWidth: 75,
// ),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
product.costStr,
product.unitCostStr,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
// Text(
// '${product.availableUnits} × ${product.unitCostStr}',
// style: TextStyle(
// fontSize: 12,
// color: Colors.grey,
// ),
// ),
Text(
'${product.quantity} × ${product.unitCostStr}',
'per unit',
style: TextStyle(
fontSize: 12,
color: Colors.grey,
Expand All @@ -124,7 +131,7 @@ class ProductItemTile extends StatelessWidget {
}

void _saleProduct(BuildContext context) {
if (product.quantity > 0) {
if (product.availableUnits > 0) {
SaleFormDialog.display(context, product);
}
}
Expand Down
9 changes: 4 additions & 5 deletions lib/src/pages/widgets/product_report_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class ProductReportView extends StatelessWidget {

@override
Widget build(BuildContext context) {
if (report.totalItems == 0) {
if (report.totalUnits == 0) {
return buildEmptyMessage();
}

Expand All @@ -28,10 +28,9 @@ class ProductReportView extends StatelessWidget {
children += [
Divider(height: 1),
SizedBox(height: 10),
buildItemRow(
'Total Products', '${report.items.length}', Colors.grey[700]),
buildItemRow('Products', '${report.items.length}', Colors.grey[700]),
SizedBox(height: 10),
buildItemRow('Total Quantity', '${report.totalItems}', Colors.grey[700]),
buildItemRow('Total Units', '${report.totalUnits}', Colors.grey[700]),
SizedBox(height: 10),
Divider(height: 1),
SizedBox(height: 10),
Expand Down Expand Up @@ -106,7 +105,7 @@ class ProductReportView extends StatelessWidget {
),
Text(' = '),
Text(
product.costStr.padRight(10, ' '),
product.totalCostStr.padRight(10, ' '),
style: TextStyle(
fontFamily: 'monospace',
fontSize: 12,
Expand Down
2 changes: 1 addition & 1 deletion lib/src/pages/widgets/report_page_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class ReportPageWidget<T extends Report> extends StatelessWidget {
}

Widget buildSaveAsPdfButton(BuildContext context, T report) {
if (report.totalItems == 0) {
if (report.totalUnits == 0) {
return Container();
}
return ListTile(
Expand Down
Loading

0 comments on commit 19738a5

Please sign in to comment.