Скидки

null-safety-migration
suvaysov 2023-06-20 10:28:37 +06:00
parent 8122dc94db
commit d528e44abc
16 changed files with 712 additions and 406 deletions

View File

@ -19,6 +19,7 @@
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">

View File

@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.5.31'
ext.kotlin_version = '1.8.22'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip

View File

@ -7,7 +7,9 @@ class DialogRequest {
required this.buttonTitle,
this.cancelTitle,
this.requestPrice,
this.requestCount
this.requestCount,
this.requestDiscount,
this.requestDiscountPercent,
});
final String title;
final String description;
@ -16,6 +18,8 @@ class DialogRequest {
final String? requestPrice;
final String? requestCount;
final String? requestDiscount;
final bool? requestDiscountPercent;
}
@ -25,10 +29,15 @@ class DialogResponse {
required this.confirmed,
this.responsePrice,
this.responseCount,
this.responseDiscount,
this.responseDiscountPercent,
});
final String? responsePrice;
final String? responseCount;
final bool confirmed;
final String? responseDiscount;
final bool? responseDiscountPercent;
}

View File

@ -8,6 +8,8 @@ class ProductDao {
categoryId = map['categoryId'] as int? ;
count = map['count'] as double;
price = map['price'] as double;
discount = cast<double>(map['discount']);
discountPercent = cast<bool>(map['discountPercent']);
productName = cast<String>(map['productName']) ?? '';
categoryName = cast<String>(map['categoryName']);
eanCode = cast<String>(map['eanCode']);
@ -20,6 +22,8 @@ class ProductDao {
int? categoryId;
double? count;
double? price;
double? discount;
bool? discountPercent;
String productName = '';
String? categoryName;
String? eanCode;
@ -33,6 +37,8 @@ class ProductDao {
'categoryId': categoryId,
'count': count,
'price': price,
'discount': discount,
'discountPercent': discountPercent,
'productName': productName,
'categoryName': categoryName,
'eanCode': eanCode,

View File

@ -14,6 +14,7 @@ class ItemBean {
String? articul;
double? cnt;
double? price;
double? discount;
String? excise;
static ItemBean fromMap(dynamic map) {
@ -23,6 +24,7 @@ class ItemBean {
itemsBean.articul = cast<String>(map['articul']);
itemsBean.cnt = cast<double>(map['cnt']);
itemsBean.price = cast<double>(map['price']);
itemsBean.discount = cast<double>(map['discount']);
itemsBean.excise = cast<String>(map['excise']);
return itemsBean;
}
@ -33,6 +35,7 @@ class ItemBean {
'articul': articul,
'cnt': cnt,
'price': price,
'discount': discount,
'excise': excise,
};
}

View File

@ -31,7 +31,7 @@ final Logger log = getLogger('SetSellStateAction');
final DbService _dbService = locator<DbService>();
ThunkAction<AppState> counterOrEditSellItem(
{required int transactionId, required double counter, double? price}) {
{required int transactionId, required double counter, double? price , double? discount ,bool? discountPercent }) {
return (Store<AppState> store) async {
log.i(
'counterSellItem id = $transactionId counter = $counter, price = $price',
@ -50,6 +50,17 @@ ThunkAction<AppState> counterOrEditSellItem(
final String val = ((item.count ?? 0) + counter).toStringAsFixed(5);
item.count = double.parse(val);
}
if(discountPercent != null)
item.discountPercent = discountPercent;
if(discount != null) {
if(discountPercent == true && discount! > 100) {
discount = 100;
} else if(item.price! < discount!) {
discount = item.price;
}
item.discount = discount;
}
transactionRec.data = jsonEncode(item.toMap());
_dbService.update(transactionRecTableName, transactionRec.toMap());
}

View File

@ -56,6 +56,8 @@ class DialogService {
required String title,
required String requestCount,
String? requestPrice,
String? requestDiscount,
bool requestDiscountPercent = false,
String? description,
String confirmationTitle = 'ПОДТВЕРДИТЬ',
String cancelTitle = 'Отмена',
@ -67,7 +69,10 @@ class DialogService {
buttonTitle: confirmationTitle,
cancelTitle: cancelTitle,
requestPrice: requestPrice,
requestCount: requestCount));
requestCount: requestCount,
requestDiscount: requestDiscount,
requestDiscountPercent: requestDiscountPercent
));
return _dialogCompleter!.future;
}

View File

@ -17,6 +17,7 @@ import 'package:satu/core/redux/store.dart';
import 'package:satu/core/utils/locator.dart';
import 'package:uuid/uuid.dart';
import '../../views/work/tabs/utils/product_utils.dart';
import '../models/dictionary/contragent/contragent_response_entity.dart';
import 'api_service.dart';
import 'db_service.dart';
@ -172,6 +173,10 @@ class SellService extends BaseService {
..name = product.productName
..excise = product.excise
..articul = product.article?.toString();
if(product.discount != null) {
var priceWithDiscount = sumPriceByDiscount(product.price!, product.discount!, product.discountPercent);
item.discount = product.price! - priceWithDiscount;
}
return item;
}

View File

@ -88,7 +88,7 @@ class _LoginViewState extends State<LoginView> {
),
verticalSpaceLarge,
IconButton(
icon: const Icon(MdiIcons.qrcodeScan),
icon: Icon(MdiIcons.qrcodeScan),
iconSize: ScreenUtil().setSp(40.0),
tooltip: 'Scan',
onPressed: scan,

View File

@ -9,9 +9,11 @@ import 'package:satu/core/utils/locator.dart';
import 'package:satu/core/utils/utils_parse.dart';
import 'package:satu/shared/app_colors.dart';
import 'package:satu/shared/shared_styles.dart';
import 'package:satu/shared/ui_helpers.dart';
import 'package:satu/widgets/ui/product_title_widget.dart';
import '../../../views/select_by_scan/add_by_barcode_view.dart';
import '../../utils/product_utils.dart';
class ProductSellTile extends StatefulWidget {
@ -23,7 +25,10 @@ class ProductSellTile extends StatefulWidget {
this.price,
this.count,
this.isOdd,
this.transactionId})
this.transactionId,
this.discountPercent = false,
this.discount = 0
})
: super(key: key);
final String name;
@ -31,6 +36,8 @@ class ProductSellTile extends StatefulWidget {
final String? categoryName;
final num? price;
final num? count;
final num discount;
final bool discountPercent;
final bool? isOdd;
final int? transactionId;
@ -41,11 +48,6 @@ class ProductSellTile extends StatefulWidget {
class _ProductSellTileState extends State<ProductSellTile> {
final DialogService _dialogService = locator<DialogService>();
void _onItemTapped(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => const SelectByScanView()));
}
@override
Widget build(BuildContext context) {
return Dismissible(
@ -103,12 +105,28 @@ class _ProductSellTileState extends State<ProductSellTile> {
children: [
Padding(
padding: const EdgeInsets.only(bottom: 3.0),
child: Text(
'${widget.price}',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: textColor),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if(widget.discount > 0)
Text(
'${sumPriceByDiscount(widget.price!.toDouble()
, widget.discount.toDouble(), widget.discountPercent)} ',
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: dangerColor),
),
if(widget.discount > 0)
horizontalSpaceTiny,
Text(
'${widget.price}',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: textColor),
),
],
),
),
Row(
@ -206,14 +224,20 @@ class _ProductSellTileState extends State<ProductSellTile> {
await _dialogService.showConfirmationDialogInput(
title: widget.name,
requestCount: formatDecimal(widget.count!.toDouble()),
requestPrice: formatDecimal(widget.price!.toDouble()));
requestPrice: formatDecimal(widget.price!.toDouble()),
requestDiscount: formatDecimal(widget.discount.toDouble()),
requestDiscountPercent: widget.discountPercent == true,
);
if (response.confirmed) {
if (isNumeric(response.responsePrice) &&
isNumeric(response.responseCount)) {
Redux.store!.dispatch(counterOrEditSellItem(
transactionId: widget.transactionId!,
counter: double.parse(response.responseCount!),
price: double.parse(response.responsePrice!),
discount: double.parse(response.responseDiscount! == '' ? '0' : response.responseDiscount!),
discountPercent: response.responseDiscountPercent,
));
} else {
_dialogService.showDialog(description: 'Не верный формат');

View File

@ -69,10 +69,12 @@ class SellView extends StatelessWidget {
final ProductDao product =
state.items!.elementAt(index);
return ProductSellTile(
key: UniqueKey(),
key: product.id !=null ? Key(product.id.toString()) : UniqueKey(),
ean: product.eanCode,
isOdd: index % 2 == 0,
name: product.productName,
discount: product.discount ?? 0,
discountPercent: product.discountPercent ?? false,
price: product.price,
count: product.count,
categoryName: product.categoryName,

View File

@ -4,10 +4,24 @@ double sumProducts(List<ProductDao> list) {
double result = 0.0;
if (list.isNotEmpty) {
for( final ProductDao product in list) {
final String val = (product.price! * product.count!).toStringAsFixed(5);
double price = product.price! * product.count!;
double discount = product.discount ?? 0;
bool discountPercent = product.discountPercent ?? false;
price = sumPriceByDiscount(price, discount, discountPercent);
final String val = (price).toStringAsFixed(5);
// result += product.price! * product.count!;
result += double.parse(val);
}
}
return result;
}
double sumPriceByDiscount(double price, double? discount, bool? discountPercent) {
double result = 0.0;
if(discountPercent != null && discountPercent) {
result = price - (price * (discount ?? 0) / 100);
} else {
result = price - (discount ?? 0);
}
return result;
}

View File

@ -20,12 +20,15 @@ class _DialogManagerState extends State<DialogManager> {
final DialogService _dialogService = locator<DialogService>();
late TextEditingController _controllerPrice;
late TextEditingController _controllerCount;
late TextEditingController _controllerDiscount;
@override
void initState() {
super.initState();
_controllerPrice = TextEditingController();
_controllerCount = TextEditingController();
_controllerDiscount = TextEditingController();
_dialogService.registerDialogListener(_showDialog, _showDialogInput);
}
@ -33,6 +36,7 @@ class _DialogManagerState extends State<DialogManager> {
void dispose() {
_controllerPrice.dispose();
_controllerCount.dispose();
_controllerDiscount.dispose();
super.dispose();
}
@ -74,10 +78,16 @@ class _DialogManagerState extends State<DialogManager> {
onPressed: () {
final String _price = _controllerPrice.text;
final String _count = _controllerCount.text;
_dialogService.dialogComplete(DialogResponse(
confirmed: true,
responsePrice: _price.replaceAll(',', '.'),
responseCount: _count.replaceAll(',', '.')));
responseCount: _count.replaceAll(',', '.'),
)
);
},
),
),
@ -104,91 +114,149 @@ class _DialogManagerState extends State<DialogManager> {
void _showDialogInput(DialogRequest request) {
final bool isConfirmationDialog = request.cancelTitle != null;
_controllerPrice.text = request.requestPrice ?? '';
_controllerCount.text = request.requestCount ?? '';
_controllerDiscount.text = request.requestDiscount ?? '';
final dialogController = showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: backgroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
actionsPadding: const EdgeInsets.only(right: 15, bottom: 5),
content: SizedBox(
width: ScreenUtil().setWidth(ScreenUtil().screenWidth * 0.9),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
request.title,
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: ScreenUtil().setSp(14)),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
verticalSpaceSmall,
const Divider(),
verticalSpaceSmall,
Visibility(
visible: request.requestPrice != null,
child: InputFieldRounded(
controller: _controllerPrice,
placeholder: '',
suffixText: '',
labelText: 'Стоимость товара',
textInputType:
const TextInputType.numberWithOptions(decimal: true),
),
),
InputFieldRounded(
textInputType:
const TextInputType.numberWithOptions(decimal: true),
controller: _controllerCount,
placeholder: '',
suffixText: 'шт',
labelText: 'Количество товара',
),
verticalSpaceSmall,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (isConfirmationDialog)
Expanded(
child: TextButton(
onPressed: () {
_dialogService.dialogComplete(
DialogResponse(confirmed: false));
},
//color: redColor,
child: Text(
request.cancelTitle!,
style: TextStyle(
fontSize: 14.sp, color: placeholderColor),
),
),
),
Expanded(
child: BusyButton(
title: request.buttonTitle,
onPressed: () {
final String _price = _controllerPrice.text;
final String _count = _controllerCount.text;
_dialogService.dialogComplete(DialogResponse(
confirmed: true,
responsePrice: _price.replaceAll(',', '.'),
responseCount: _count.replaceAll(',', '.')));
},
builder: (context) {
bool discountPercent = request.requestDiscountPercent ?? false;
return StatefulBuilder(
builder: (stfContext, stfSetState) {
return AlertDialog(
backgroundColor: backgroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
actionsPadding: const EdgeInsets.only(right: 15, bottom: 5),
content: SizedBox(
width: ScreenUtil().setWidth(ScreenUtil().screenWidth * 0.9),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
request.title,
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: ScreenUtil().setSp(14)),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
verticalSpaceSmall,
const Divider(),
verticalSpaceSmall,
Visibility(
visible: request.requestPrice != null,
child: InputFieldRounded(
controller: _controllerPrice,
placeholder: '',
suffixText: '',
labelText: 'Стоимость товара',
textInputType:
const TextInputType.numberWithOptions(decimal: true),
),
),
InputFieldRounded(
textInputType:
const TextInputType.numberWithOptions(decimal: true),
controller: _controllerCount,
placeholder: '',
suffixText: 'шт',
labelText: 'Количество товара',
),
if(request.requestDiscount != null)
Column(
children: [
verticalSpaceSmall,
Row(
children: [
const Text(
'Скидка',
style: TextStyle(
fontSize: 12, color: placeholderColor),
),
horizontalSpaceMedium,
const Text(
'',
style: TextStyle(
fontSize: 12, color: placeholderColor),
),
Switch(
activeColor: Colors.white,
inactiveThumbColor: Colors.white,
activeTrackColor: primaryColor.withOpacity(0.5),
inactiveTrackColor: primaryColor.withOpacity(0.5),
value: discountPercent,
onChanged: (vl) {
stfSetState(() {
discountPercent = vl;
});
},
),
const Text(
'%',
style: TextStyle(
fontSize: 12, color: placeholderColor),
),
],
),
verticalSpaceTiny,
InputFieldRounded(
textInputType:
const TextInputType.numberWithOptions(decimal: true),
controller: _controllerDiscount,
placeholder: '',
suffixText: discountPercent ? '%' : '',
labelText: 'Скидка',
),
],
),
verticalSpaceSmall,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (isConfirmationDialog)
Expanded(
child: TextButton(
onPressed: () {
_dialogService.dialogComplete(
DialogResponse(confirmed: false));
},
//color: redColor,
child: Text(
request.cancelTitle!,
style: TextStyle(
fontSize: 14.sp, color: placeholderColor),
),
),
),
Expanded(
child: BusyButton(
title: request.buttonTitle,
onPressed: () {
final String _price = _controllerPrice.text;
final String _count = _controllerCount.text;
final String _discount = _controllerDiscount.text;
_dialogService.dialogComplete(DialogResponse(
confirmed: true,
responsePrice: _price.replaceAll(',', '.'),
responseCount: _count.replaceAll(',', '.'),
responseDiscount: _discount.replaceAll(',', '.'),
responseDiscountPercent: discountPercent,
));
},
),
),
],
)
],
)
],
),
),
));
),
),
);
}
);
});
dialogController.whenComplete(() {
//hook when press overlay and response not completed
if (_dialogService.completer != null) {

File diff suppressed because it is too large Load Diff

View File

@ -15,11 +15,11 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.1+2
version: 1.0.2+3
environment:
sdk: ">=2.18.2 <3.0.0"
sdk: '>=3.0.5 <4.0.0'
dependencies:
flutter:
@ -30,30 +30,30 @@ dependencies:
redux_thunk: ^0.4.0
redux_persist: ^0.9.0
redux_persist_flutter: ^0.9.0
responsive_builder: ^0.4.3
provider: ^6.0.4
logger: ^1.1.0
get_it: ^7.2.0
responsive_builder: ^0.7.0
provider: ^6.0.5
logger: ^1.4.0
get_it: ^7.6.0
equatable: ^2.0.5
http: ^0.13.5
sqflite: ^2.2.0+3
path_provider: ^2.0.11
material_design_icons_flutter: ^5.0.6996
intl: ^0.17.0
http: ^1.0.0
sqflite: ^2.2.8+4
path_provider: ^2.0.15
material_design_icons_flutter: ^7.0.7296
intl: ^0.18.1
device_info: ^2.0.3
auto_size_text: ^3.0.0
url_launcher: ^6.1.6
qr_flutter: ^4.0.0
url_launcher: ^6.1.11
qr_flutter: ^4.1.0
mask_text_input_formatter: ^2.4.0
flutter_screenutil: ^5.6.0
shared_preferences: ^2.0.15
flutter_screenutil: ^5.8.4
shared_preferences: ^2.1.2
material_floating_search_bar: ^0.3.7
implicitly_animated_reorderable_list: ^0.4.2
uuid: ^3.0.6
charset_converter: ^2.1.0
uuid: ^3.0.7
charset_converter: ^2.1.1
ai_barcode: ^3.2.4
permission_handler: ^10.2.0
flutter_svg: ^1.1.6
permission_handler: ^10.3.0
flutter_svg: ^2.0.7
grouped_list: ^5.1.2
infinite_scroll_pagination: ^3.2.0
flutter_bluetooth_basic: ^0.1.7
@ -61,10 +61,10 @@ dependencies:
esc_pos_utils: ^1.1.0
esc_pos_bluetooth: ^0.4.1
dev_dependencies:
build_runner: ^2.3.2
build_runner: ^2.4.5
flutter_test:
sdk: flutter
json_serializable: ^6.5.4
json_serializable: ^6.7.0
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is