2026-02-07 15:57:09 +07:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
|
import 'package:hugeicons/hugeicons.dart';
|
|
|
|
|
import 'package:mobile_pos/Screens/Sales/provider/sales_cart_provider.dart';
|
|
|
|
|
import 'package:mobile_pos/Screens/Sales/sales_add_to_cart_sales_widget.dart';
|
|
|
|
|
import 'package:mobile_pos/constant.dart';
|
2026-02-08 14:58:52 +07:00
|
|
|
import 'package:mobile_pos/currency.dart';
|
2026-02-07 15:57:09 +07:00
|
|
|
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
|
|
|
|
|
|
|
|
|
class SalesCartListWidget extends ConsumerWidget {
|
|
|
|
|
const SalesCartListWidget({super.key});
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
|
|
|
final providerData = ref.watch(cartNotifier);
|
|
|
|
|
final s = lang.S.of(context);
|
|
|
|
|
final _theme = Theme.of(context);
|
|
|
|
|
return providerData.cartItemList.isNotEmpty
|
|
|
|
|
? Padding(
|
|
|
|
|
padding: const EdgeInsets.only(bottom: 12),
|
|
|
|
|
child: Theme(
|
|
|
|
|
data: Theme.of(context).copyWith(dividerColor: Colors.transparent),
|
|
|
|
|
child: ExpansionTile(
|
|
|
|
|
initiallyExpanded: true,
|
|
|
|
|
collapsedBackgroundColor: kMainColor2,
|
|
|
|
|
backgroundColor: kMainColor2,
|
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
|
borderRadius: BorderRadius.circular(8),
|
|
|
|
|
side: BorderSide(
|
|
|
|
|
color: kLineColor,
|
|
|
|
|
width: 1,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
title: Text(
|
|
|
|
|
lang.S.of(context).itemAdded,
|
|
|
|
|
style: _theme.textTheme.titleMedium,
|
|
|
|
|
),
|
|
|
|
|
children: [
|
|
|
|
|
Container(
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
child: ListView.builder(
|
|
|
|
|
shrinkWrap: true,
|
|
|
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
|
|
|
itemCount: providerData.cartItemList.length,
|
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
|
final item = providerData.cartItemList[index];
|
|
|
|
|
|
|
|
|
|
// Calculate values for display
|
|
|
|
|
final double quantity = item.quantity.toDouble();
|
|
|
|
|
final double unitPrice = (item.unitPrice ?? 0).toDouble();
|
|
|
|
|
final double discountPerUnit = (item.discountAmount ?? 0).toDouble();
|
|
|
|
|
final double totalDiscount = quantity * discountPerUnit;
|
|
|
|
|
final double subTotal = quantity * unitPrice;
|
2026-02-08 14:58:52 +07:00
|
|
|
final double originalTotal = unitPrice * quantity;
|
|
|
|
|
final double itemVAT = (unitPrice * quantity * (item.perItemTaxPercentage ?? 0)) / 100;
|
|
|
|
|
final double finalTotal = originalTotal + itemVAT;
|
2026-02-07 15:57:09 +07:00
|
|
|
|
2026-02-08 14:58:52 +07:00
|
|
|
return Padding(
|
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 10.0),
|
|
|
|
|
child: SingleChildScrollView(
|
|
|
|
|
scrollDirection: Axis.horizontal,
|
|
|
|
|
child: Row(
|
|
|
|
|
children: [
|
|
|
|
|
// 1. Product Name (Clickable to Edit)
|
|
|
|
|
GestureDetector(
|
|
|
|
|
onTap: () => showModalBottomSheet(
|
|
|
|
|
isScrollControlled: true,
|
|
|
|
|
context: context,
|
|
|
|
|
builder: (context2) {
|
|
|
|
|
return Column(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
children: [
|
|
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
|
|
|
|
child: Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
children: [
|
|
|
|
|
Text(
|
|
|
|
|
s.updateProduct,
|
|
|
|
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
|
|
|
),
|
|
|
|
|
CloseButton(
|
|
|
|
|
onPressed: () => Navigator.pop(context2),
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const Divider(thickness: 1, color: kBorderColorTextField),
|
|
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.all(16.0),
|
|
|
|
|
child: SalesAddToCartForm(
|
|
|
|
|
batchWiseStockModel: item,
|
|
|
|
|
previousContext: context2,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
child: SizedBox(
|
|
|
|
|
width: 140,
|
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
2026-02-07 15:57:09 +07:00
|
|
|
children: [
|
|
|
|
|
Text(
|
2026-02-08 14:58:52 +07:00
|
|
|
item.productName.toString(),
|
|
|
|
|
style: _theme.textTheme.titleMedium?.copyWith(
|
|
|
|
|
fontWeight: FontWeight.w500,
|
|
|
|
|
),
|
|
|
|
|
maxLines: 2,
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
2026-02-07 15:57:09 +07:00
|
|
|
),
|
2026-02-08 14:58:52 +07:00
|
|
|
if (item.productType == 'variant')
|
|
|
|
|
Text(
|
|
|
|
|
'[${item.batchName}]',
|
|
|
|
|
style: const TextStyle(fontSize: 10, fontStyle: FontStyle.italic, color: Colors.grey),
|
|
|
|
|
),
|
2026-02-07 15:57:09 +07:00
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
2026-02-08 14:58:52 +07:00
|
|
|
),
|
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
|
|
|
|
|
|
// 2. Decrease Icon
|
|
|
|
|
GestureDetector(
|
|
|
|
|
onTap: () => providerData.quantityDecrease(index),
|
|
|
|
|
child: Container(
|
|
|
|
|
padding: const EdgeInsets.all(4),
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: kMainColor.withOpacity(0.1),
|
|
|
|
|
borderRadius: BorderRadius.circular(4),
|
2026-02-07 15:57:09 +07:00
|
|
|
),
|
2026-02-08 14:58:52 +07:00
|
|
|
child: const Icon(Icons.remove, size: 16, color: kMainColor),
|
2026-02-07 15:57:09 +07:00
|
|
|
),
|
2026-02-08 14:58:52 +07:00
|
|
|
),
|
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
|
|
|
|
|
|
// 3. Qty Order
|
|
|
|
|
Text(
|
|
|
|
|
formatPointNumber(quantity),
|
|
|
|
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
|
|
|
|
|
|
// 4. Increase Icon
|
|
|
|
|
GestureDetector(
|
|
|
|
|
onTap: () => providerData.quantityIncrease(index),
|
|
|
|
|
child: Container(
|
|
|
|
|
padding: const EdgeInsets.all(4),
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: kMainColor.withOpacity(0.1),
|
|
|
|
|
borderRadius: BorderRadius.circular(4),
|
|
|
|
|
),
|
|
|
|
|
child: const Icon(Icons.add, size: 16, color: kMainColor),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
|
|
|
|
|
|
// 5. Value Original
|
|
|
|
|
Text(
|
|
|
|
|
'$currency${formatPointNumber(originalTotal)}',
|
|
|
|
|
style: TextStyle(
|
2026-02-07 15:57:09 +07:00
|
|
|
color: kPeraColor,
|
2026-02-08 14:58:52 +07:00
|
|
|
fontSize: 12,
|
2026-02-07 15:57:09 +07:00
|
|
|
),
|
|
|
|
|
),
|
2026-02-08 14:58:52 +07:00
|
|
|
const SizedBox(width: 8),
|
|
|
|
|
|
|
|
|
|
// 6. Value VAT/Tax
|
|
|
|
|
if (itemVAT > 0) ...[
|
|
|
|
|
Text(
|
|
|
|
|
'VAT: $currency${formatPointNumber(itemVAT)}',
|
|
|
|
|
style: TextStyle(
|
2026-02-07 15:57:09 +07:00
|
|
|
color: kPeraColor,
|
2026-02-08 14:58:52 +07:00
|
|
|
fontSize: 10,
|
2026-02-07 15:57:09 +07:00
|
|
|
),
|
|
|
|
|
),
|
2026-02-08 14:58:52 +07:00
|
|
|
const SizedBox(width: 8),
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
// 7. Sum (Value Original + VAT/Tax)
|
|
|
|
|
Text(
|
|
|
|
|
'$currency${formatPointNumber(finalTotal)}',
|
2026-02-07 15:57:09 +07:00
|
|
|
style: _theme.textTheme.titleSmall?.copyWith(
|
2026-02-08 14:58:52 +07:00
|
|
|
color: kMainColor,
|
|
|
|
|
fontWeight: FontWeight.bold,
|
2026-02-07 15:57:09 +07:00
|
|
|
),
|
|
|
|
|
),
|
2026-02-08 14:58:52 +07:00
|
|
|
const SizedBox(width: 12),
|
|
|
|
|
|
|
|
|
|
// 8. Delete Icon
|
|
|
|
|
GestureDetector(
|
|
|
|
|
onTap: () => providerData.deleteToCart(index),
|
|
|
|
|
child: HugeIcon(
|
|
|
|
|
icon: HugeIcons.strokeRoundedDelete03,
|
|
|
|
|
size: 20,
|
|
|
|
|
color: Colors.red,
|
2026-02-07 15:57:09 +07:00
|
|
|
),
|
2026-02-08 14:58:52 +07:00
|
|
|
),
|
2026-02-07 15:57:09 +07:00
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
: SizedBox.shrink();
|
|
|
|
|
}
|
|
|
|
|
}
|