perubahan denomuretor on pos sale

This commit is contained in:
2026-04-04 23:44:16 +07:00
parent 517ed3bea3
commit d912b1f63a
20 changed files with 350 additions and 142 deletions

View File

@@ -23,7 +23,6 @@ import '../../Const/api_config.dart';
import '../../GlobalComponents/glonal_popup.dart';
import '../../Repository/API/future_invoice.dart';
import '../../constant.dart';
import '../../currency.dart';
import '../../model/add_to_cart_model.dart';
import '../../model/sale_transaction_model.dart';
import '../../widgets/multipal payment mathods/multi_payment_widget.dart';
@@ -35,6 +34,16 @@ import '../invoice_details/sales_invoice_details_screen.dart';
import '../vat_&_tax/model/vat_model.dart';
import '../vat_&_tax/provider/text_repo.dart';
/// Format a number as Indonesian Rupiah for display only.
/// Example: 12500.75 → "Rp 12.500"
String _formatRupiah(num value) {
final intStr = value.round().toString().replaceAllMapped(
RegExp(r'(\d)(?=(\d{3})+(?!\d))'),
(m) => '${m[1]}.',
);
return 'Rp $intStr';
}
class AddSalesScreen extends ConsumerStatefulWidget {
AddSalesScreen({
super.key,
@@ -70,6 +79,11 @@ class AddSalesScreenState extends ConsumerState<AddSalesScreen> {
void initState() {
super.initState();
// Pre-populate phone from selected customer
if (widget.customerModel?.phone != null) {
phoneController.text = widget.customerModel!.phone!;
}
// Listener for Received Amount Controller to calculate prices
recevedAmountController.addListener(() {
final cart = ref.read(cartNotifier);
@@ -296,7 +310,9 @@ class AddSalesScreenState extends ConsumerState<AddSalesScreen> {
children: [
Text(lang.S.of(context).dueAmount),
Text(
widget.customerModel?.due == null ? '$currency 0' : '$currency${widget.customerModel?.due}',
widget.customerModel?.due == null
? _formatRupiah(0)
: _formatRupiah(widget.customerModel!.due ?? 0),
style: const TextStyle(color: Color(0xFFFF8C34)),
),
],
@@ -304,30 +320,34 @@ class AddSalesScreenState extends ConsumerState<AddSalesScreen> {
const SizedBox(
height: 10,
),
AppTextField(
textFieldType: TextFieldType.NAME,
readOnly: true,
initialValue: widget.customerModel?.name ?? 'Guest',
decoration: InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.always,
labelText: lang.S.of(context).customerName,
border: const OutlineInputBorder(),
),
),
Visibility(
visible: widget.customerModel == null,
child: Padding(
padding: const EdgeInsets.only(top: 20.0),
child: AppTextField(
controller: phoneController,
textFieldType: TextFieldType.PHONE,
decoration: kInputDecoration.copyWith(
floatingLabelBehavior: FloatingLabelBehavior.always,
labelText: lang.S.of(context).customerPhoneNumber,
hintText: lang.S.of(context).enterCustomerPhoneNumber,
Row(
children: [
Expanded(
child: AppTextField(
textFieldType: TextFieldType.NAME,
readOnly: true,
initialValue: widget.customerModel?.name ?? 'Guest',
decoration: InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.always,
labelText: lang.S.of(context).customerName,
border: const OutlineInputBorder(),
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: AppTextField(
controller: phoneController,
textFieldType: TextFieldType.PHONE,
readOnly: widget.customerModel != null,
decoration: kInputDecoration.copyWith(
floatingLabelBehavior: FloatingLabelBehavior.always,
labelText: lang.S.of(context).customerPhoneNumber,
hintText: lang.S.of(context).enterCustomerPhoneNumber,
),
),
),
],
),
],
),
@@ -385,7 +405,7 @@ class AddSalesScreenState extends ConsumerState<AddSalesScreen> {
),
),
Text(
'$currency${formatPointNumber(providerData.totalAmount)}',
_formatRupiah(providerData.totalAmount),
style: _theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w500,
),
@@ -584,30 +604,17 @@ class AddSalesScreenState extends ConsumerState<AddSalesScreen> {
const SizedBox(width: 10),*/
// VAT Amount Input Field
// VAT Amount Display (readOnly — show as Rupiah)
SizedBox(
height: 30,
width: 100,
child: TextFormField(
controller: providerData.vatAmountController,
style: _theme.textTheme.titleSmall,
readOnly: true,
onChanged: (value) => providerData.calculateDiscount(
value: value,
selectedTaxType: discountType.toString(),
child: Align(
alignment: Alignment.centerRight,
child: Text(
_formatRupiah(num.tryParse(providerData.vatAmountController.text) ?? 0),
style: _theme.textTheme.titleSmall,
textAlign: TextAlign.right,
),
textAlign: TextAlign.right,
decoration: InputDecoration(
hintText: '0',
hintStyle: _theme.textTheme.titleSmall?.copyWith(
color: kPeraColor,
),
border: const UnderlineInputBorder(borderSide: BorderSide(color: kBorder)),
enabledBorder: const UnderlineInputBorder(borderSide: BorderSide(color: kBorder)),
focusedBorder: const UnderlineInputBorder(),
contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 8),
),
keyboardType: TextInputType.number,
),
),
],
@@ -663,7 +670,7 @@ class AddSalesScreenState extends ConsumerState<AddSalesScreen> {
),
),
Text(
formatPointNumber(providerData.actualTotalAmount),
_formatRupiah(providerData.actualTotalAmount),
style: _theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w600,
),
@@ -690,7 +697,7 @@ class AddSalesScreenState extends ConsumerState<AddSalesScreen> {
),
),
Text(
formatPointNumber(providerData.roundingAmount),
_formatRupiah(providerData.roundingAmount),
style: _theme.textTheme.titleSmall?.copyWith(
color: kPeraColor,
),
@@ -710,7 +717,7 @@ class AddSalesScreenState extends ConsumerState<AddSalesScreen> {
),
),
Text(
formatPointNumber(providerData.totalPayableAmount),
_formatRupiah(providerData.totalPayableAmount),
style: _theme.textTheme.titleSmall?.copyWith(
color: kPeraColor,
),
@@ -734,27 +741,45 @@ class AddSalesScreenState extends ConsumerState<AddSalesScreen> {
color: kPeraColor,
),
),
SizedBox(
width: context.width() / 4,
height: 30,
child: TextFormField(
controller: recevedAmountController,
readOnly: (paymentWidgetKey.currentState?.getPaymentEntries().length ?? 1) > 1,
keyboardType: TextInputType.number,
textAlign: TextAlign.right,
style: _theme.textTheme.titleSmall,
decoration: InputDecoration(
hintText: '0',
hintStyle: _theme.textTheme.titleSmall?.copyWith(
color: kPeraColor,
// Received Amount — show Rupiah format when readOnly, numeric input when editable
Builder(builder: (ctx) {
final isReadOnly = (paymentWidgetKey.currentState?.getPaymentEntries().length ?? 1) > 1;
if (isReadOnly) {
return SizedBox(
width: context.width() / 4,
height: 30,
child: Align(
alignment: Alignment.centerRight,
child: Text(
_formatRupiah(num.tryParse(recevedAmountController.text) ?? 0),
style: _theme.textTheme.titleSmall,
textAlign: TextAlign.right,
),
),
);
}
return SizedBox(
width: context.width() / 4,
height: 30,
child: TextFormField(
controller: recevedAmountController,
readOnly: false,
keyboardType: TextInputType.number,
textAlign: TextAlign.right,
style: _theme.textTheme.titleSmall,
decoration: InputDecoration(
hintText: '0',
hintStyle: _theme.textTheme.titleSmall?.copyWith(
color: kPeraColor,
),
border: UnderlineInputBorder(borderSide: BorderSide(color: kBorder)),
enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: kBorder)),
focusedBorder: UnderlineInputBorder(),
contentPadding: EdgeInsets.symmetric(horizontal: 0, vertical: 8),
),
border: UnderlineInputBorder(borderSide: BorderSide(color: kBorder)),
enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: kBorder)),
focusedBorder: UnderlineInputBorder(),
contentPadding: EdgeInsets.symmetric(horizontal: 0, vertical: 8),
),
),
),
);
}),
],
),
),
@@ -774,7 +799,7 @@ class AddSalesScreenState extends ConsumerState<AddSalesScreen> {
),
),
Text(
formatPointNumber(providerData.changeAmount),
_formatRupiah(providerData.changeAmount),
style: _theme.textTheme.titleSmall?.copyWith(
color: kPeraColor,
),
@@ -800,7 +825,7 @@ class AddSalesScreenState extends ConsumerState<AddSalesScreen> {
),
),
Text(
formatPointNumber(providerData.dueAmount),
_formatRupiah(providerData.dueAmount),
style: _theme.textTheme.titleSmall?.copyWith(
color: kPeraColor,
),
@@ -820,6 +845,7 @@ class AddSalesScreenState extends ConsumerState<AddSalesScreen> {
key: paymentWidgetKey,
showWalletOption: true,
totalAmountController: recevedAmountController,
totalDueAmount: providerData.totalPayableAmount,
showChequeOption: true,
showCashDenomination: true,
initialTransactions: widget.transitionModel?.transactions,

View File

@@ -4,9 +4,18 @@ 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';
import 'package:mobile_pos/currency.dart';
import 'package:mobile_pos/generated/l10n.dart' as lang;
/// Format a number as Indonesian Rupiah for display only.
/// Example: 12500.75 → "Rp 12.500"
String _formatRupiah(num value) {
final intStr = value.round().toString().replaceAllMapped(
RegExp(r'(\d)(?=(\d{3})+(?!\d))'),
(m) => '${m[1]}.',
);
return 'Rp $intStr';
}
class SalesCartListWidget extends ConsumerWidget {
const SalesCartListWidget({super.key});
@@ -137,7 +146,7 @@ class SalesCartListWidget extends ConsumerWidget {
// 3. Qty Order
Text(
formatPointNumber(quantity),
formatPointNumber(quantity, addComma: true),
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(width: 8),
@@ -158,7 +167,7 @@ class SalesCartListWidget extends ConsumerWidget {
// 5. Value Original
Text(
'$currency${formatPointNumber(originalTotal)}',
_formatRupiah(originalTotal),
style: TextStyle(
color: kPeraColor,
fontSize: 12,
@@ -169,7 +178,7 @@ class SalesCartListWidget extends ConsumerWidget {
// 6. Value VAT/Tax
if (itemVAT > 0) ...[
Text(
'VAT: $currency${formatPointNumber(itemVAT)}',
'VAT: ${_formatRupiah(itemVAT)}',
style: TextStyle(
color: kPeraColor,
fontSize: 10,
@@ -180,7 +189,7 @@ class SalesCartListWidget extends ConsumerWidget {
// 7. Sum (Value Original + VAT/Tax)
Text(
'$currency${formatPointNumber(finalTotal)}',
_formatRupiah(finalTotal),
style: _theme.textTheme.titleSmall?.copyWith(
color: kMainColor,
fontWeight: FontWeight.bold,

View File

@@ -317,7 +317,7 @@ class _ProductCardState extends State<ProductCard> {
),
// if (permissionService.hasPermission(Permit.salesPriceView.value))
Text(
'$currency${widget.productPrice}',
'$currency ${formatPointNumber(widget.productPrice, addComma: true)}',
style: theme.textTheme.titleMedium!.copyWith(fontSize: 18),
),
],