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,