mobile_skt/lib/screens/sale_quotation/sale_quotation.controller.dart

415 lines
16 KiB
Dart
Raw Normal View History

import 'dart:convert';
import 'package:decimal/decimal.dart';
2024-04-02 12:24:08 +08:00
import 'package:flutter/material.dart';
2024-04-01 17:35:34 +08:00
import 'package:get/get.dart';
import 'package:math_expressions/math_expressions.dart';
2024-04-12 09:56:00 +08:00
import 'package:sk_base_mobile/apis/api.dart';
import 'package:sk_base_mobile/models/base_search_more_controller.dart';
2024-04-12 09:56:00 +08:00
import 'package:sk_base_mobile/models/sale_quotaion_component.model.dart';
import 'package:sk_base_mobile/models/sale_quotaion_group.model.dart';
2024-04-01 17:35:34 +08:00
import 'package:sk_base_mobile/models/sale_quotation.model.dart';
2024-04-12 09:56:00 +08:00
import 'package:sk_base_mobile/models/sale_quotation_template.model.dart';
import 'package:sk_base_mobile/models/workbench.model.dart';
2024-04-02 12:24:08 +08:00
import 'package:sk_base_mobile/screens/sale_quotation/components/sale_quotation_group_search.dart';
import 'package:sk_base_mobile/services/storage.service.dart';
2024-04-12 09:56:00 +08:00
import 'package:sk_base_mobile/util/snack_bar.util.dart';
import 'package:sk_base_mobile/widgets/form_item/sk_multi_search_more.dart';
2024-04-02 12:24:08 +08:00
import 'package:sk_base_mobile/util/modal.util.dart';
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
2024-04-02 16:22:21 +08:00
import 'package:pinyin/pinyin.dart';
2024-04-01 17:35:34 +08:00
class SaleQuotationController extends GetxController {
static SaleQuotationController get to => Get.find();
final RxList editingcell = RxList([null, null, null]);
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
2024-04-01 17:35:34 +08:00
RxList<SaleQuotationModel> groups = RxList<SaleQuotationModel>([]);
2024-04-12 09:56:00 +08:00
RxDouble totalCost = RxDouble(0.0);
RxDouble totalPrice = RxDouble(0.0);
RxBool isFormulaEditing = false.obs;
RxString formula = '成本 * 1.3 / 0.864'.obs;
2024-04-12 09:56:00 +08:00
RxList<SaleQuotationTemplateModel> templates = RxList([]);
final List<WorkBenchModel> menus = [
WorkBenchModel(title: '导出明细', icon: 'export.svg'),
// WorkBenchModel(title: '模板', icon: 'sale_quotation_template.svg'),
WorkBenchModel(title: '配件管理', icon: 'product.svg'),
WorkBenchModel(title: '分组管理', icon: 'sale_quotation_group.svg'),
WorkBenchModel(title: '计算公式', icon: 'sale_quotation_formula.svg'),
];
RxString templateName = '默认'.obs;
int? templateId;
2024-04-01 17:35:34 +08:00
@override
void onReady() {
init();
2024-04-01 17:35:34 +08:00
super.onReady();
}
2024-04-02 12:24:08 +08:00
Future<void> init() async {
String? salesQuotation = StorageService.to.getString('salesQuotation');
if (salesQuotation != null) {
2024-04-12 09:56:00 +08:00
SaleQuotationTemplateModel editTemplate =
SaleQuotationTemplateModel.fromJson(jsonDecode(salesQuotation));
parseTemplateModel(editTemplate);
}
2024-04-12 09:56:00 +08:00
await getTemplates();
}
void parseTemplateModel(SaleQuotationTemplateModel editTemplate) {
groups.assignAll(editTemplate.template.data);
templateName.value = editTemplate.name ?? '';
formula.value = editTemplate.template.formula;
totalPrice.value = editTemplate.template.totalPrice?.toDouble() ?? 0.0;
totalCost.value = editTemplate.template.totalCost?.toDouble() ?? 0.0;
templateId = editTemplate.id;
}
/// 切换报价模板
void changeTemplate(SaleQuotationTemplateModel templateModel) {
parseTemplateModel(templateModel);
saveToLocal();
}
/// 获取报价模板
Future<void> getTemplates() async {
final res = await Api.getSaleQuotationTemplate();
List<SaleQuotationTemplateModel> newList = res.data!.items
.map((e) => SaleQuotationTemplateModel.fromJson(e))
.toList();
templates.assignAll(newList);
}
2024-04-12 09:56:00 +08:00
/// 实时计算总数
void calculateTotal({String? newFormula}) {
//计算groups中所有items中的amout总和
2024-04-12 09:56:00 +08:00
totalCost.value = groups.fold<double>(0, (previousValue, element) {
return previousValue +
2024-04-12 09:56:00 +08:00
element.items.fold<double>(0, (previousValue, element) {
return previousValue + element.amount;
});
});
Parser p = Parser();
Expression exp = p.parse(newFormula ?? formula.value);
Variable x = Variable('成本');
ContextModel cm = ContextModel()..bindVariable(x, Number(totalCost.value));
double eval = exp.evaluate(EvaluationType.REAL, cm);
totalPrice.value = eval.toPrecision(2);
}
/// 实时计算数量,单价,总价之间的关系
SaleQuotationItemModel calculateRow(
SaleQuotationItemModel data, String changedField) {
Decimal quantity = Decimal.fromInt(0);
Decimal cost = Decimal.fromInt(0);
Decimal amount = Decimal.fromInt(0);
if (data.quantity != 0) {
quantity = Decimal.parse('${data.quantity}');
}
if (data.cost != 0) {
cost = Decimal.parse('${data.cost}');
}
if (data.amount != 0) {
amount = Decimal.parse('${data.amount}');
}
// 入库一般是先输入单价和数量,然后计算单价
if (changedField != 'amount') {
Decimal result = cost * quantity;
2024-04-12 09:56:00 +08:00
data.amount = result != Decimal.zero ? result.toDouble() : 0;
}
if (changedField == 'amount' && quantity != Decimal.zero) {
Decimal result =
(amount / quantity).toDecimal(scaleOnInfinitePrecision: 10);
2024-04-12 09:56:00 +08:00
data.cost = result != Decimal.zero ? result.toDouble() : 0.0;
} else if (changedField != 'amount') {
Decimal result = (cost * quantity);
2024-04-12 09:56:00 +08:00
data.amount = result != Decimal.zero ? result.toDouble() : 0;
}
return data;
}
2024-04-12 09:56:00 +08:00
/// 处理行数据变化
void afterRowChanges(int groupIndex, int rowIndex,
SaleQuotationItemModel data, String changedField) {
data = calculateRow(data, changedField);
groups[groupIndex].items[rowIndex] = data;
calculateTotal();
stopEditing();
}
2024-04-12 09:56:00 +08:00
/// 停止编辑
void stopEditing() {
editingcell.assignAll([null, null, null]);
2024-04-12 09:56:00 +08:00
saveToLocal();
}
/// 保存到本地持久化
Future<void> saveToLocal() async {
Map<String, dynamic> data = {
'name': templateName.value,
'template': {
'data': groups.map((e) => e.toJson()).toList(),
'totalCost': totalCost.value,
'totalPrice': totalPrice.value,
'formula': formula.value
}
};
if (templateId != null) {
data['id'] = templateId;
}
await StorageService.to.setString('salesQuotation', jsonEncode(data));
}
2024-04-12 09:56:00 +08:00
/// 保存到数据库
Future<bool> saveToDatabase() async {
if (templateId == null) {
await Api.createSaleQuotationTemplate({
'name': templateName.value,
'template': {
'data': groups.toJson(),
'totalCost': totalCost.value,
'totalPrice': totalPrice.value,
'formula': formula.value
}
});
} else {
await Api.updateSaleQuotationTemplate(templateId!, {
'name': templateName.value,
'template': {
'data': groups.toJson(),
'totalCost': totalCost.value,
'totalPrice': totalPrice.value,
'formula': formula.value
}
});
}
await getTemplates();
await SnackBarUtil().success('已保存');
return true;
}
2024-04-12 09:56:00 +08:00
/// 删除模板
Future<void> deleteTemplate(int templateId) async {
await Api.deleteSaleQuotationTemplate(templateId);
SnackBarUtil().success('已删除');
}
/// 添加分组
void addGroup() async {
2024-04-02 12:24:08 +08:00
final controller = Get.put(GroupSearchMoreController());
// 选择组件 选择分组
ModalUtil.showGeneralDialog(
content: SkMutilSearchMore<SaleQuotationModel>(
2024-04-02 12:24:08 +08:00
controller: controller,
enablePullUp: false,
enablePullDown: true,
onOk: (List<int> indexes) {
groups.addAll(controller.list.where((element) {
return indexes.contains(controller.list.indexOf(element));
}));
2024-04-12 09:56:00 +08:00
saveToLocal();
},
leadingBuilder: (index) {
return Container(
padding: EdgeInsets.symmetric(
horizontal: ScreenAdaper.width(5),
vertical: ScreenAdaper.height(10)),
child: Row(
children: [
Text(
controller.list[index].name,
style: TextStyle(fontSize: ScreenAdaper.height(30)),
2024-04-02 12:24:08 +08:00
),
],
),
);
2024-04-02 12:24:08 +08:00
},
),
width: Get.width - ScreenAdaper.width(50))
.then((value) => Get.delete<GroupSearchController>());
calculateTotal();
2024-04-02 12:24:08 +08:00
}
2024-04-12 09:56:00 +08:00
/// 移除分组
void removeGroup(int index) {
2024-04-02 12:24:08 +08:00
groups.removeAt(index);
calculateTotal();
2024-04-12 09:56:00 +08:00
saveToLocal();
2024-04-02 12:24:08 +08:00
}
void addItems(int groupIndex) async {
2024-04-02 12:24:08 +08:00
final controller = Get.put(ItemSearchMoreController());
2024-04-12 09:56:00 +08:00
// 选择配件
2024-04-02 12:24:08 +08:00
ModalUtil.showGeneralDialog(
content: SkMutilSearchMore<SaleQuotationItemModel>(
2024-04-02 12:24:08 +08:00
controller: controller,
enablePullUp: false,
enablePullDown: true,
isDialog: true,
onOk: (List<int> indexes) {
2024-04-12 09:56:00 +08:00
groups[groupIndex]
.items
.addAll(controller.list.where((element) {
return indexes.contains(controller.list.indexOf(element));
}));
2024-04-12 09:56:00 +08:00
saveToLocal();
},
leadingBuilder: (index) {
return Container(
2024-04-07 17:32:46 +08:00
padding: EdgeInsets.symmetric(
horizontal: ScreenAdaper.width(5),
vertical: ScreenAdaper.height(10)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
controller.list[index].name,
style: TextStyle(
fontSize: ScreenAdaper.height(30)),
),
],
),
if (controller.list[index].spec != null)
Row(
children: [
Text(
'型号:${controller.list[index].spec ?? ''}',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: ScreenAdaper.height(25)),
),
],
),
Row(
children: [
Text(
'单价:¥${controller.list[index].cost}',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: ScreenAdaper.height(25)),
),
],
),
if (controller.list[index].remark != null)
Row(
children: [
Text(
'备注:${controller.list[index].remark ?? ''}',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: ScreenAdaper.height(25)),
),
],
),
]));
2024-04-02 12:24:08 +08:00
},
),
width: Get.width - ScreenAdaper.width(50))
.then((value) => Get.delete<ItemSearchMoreController>());
calculateTotal();
}
void removeItem(int groupIndex, int rowIndex) {
groups[groupIndex].items.removeAt(rowIndex);
calculateTotal();
2024-04-02 12:24:08 +08:00
}
2024-04-12 09:56:00 +08:00
void clearWorkbench() {
groups.value = [];
totalCost.value = 0.0;
totalPrice.value = 0.0;
formula.value = '成本 * 1.3 / 0.864';
templateName.value = '默认';
templateId = null;
saveToLocal();
}
2024-04-02 12:24:08 +08:00
}
2024-04-12 09:56:00 +08:00
class GroupSearchMoreController
extends BaseSearchMoreController<SaleQuotationModel> {
2024-04-02 12:24:08 +08:00
@override
Future<List<SaleQuotationModel>> getData({bool isRefresh = false}) async {
2024-04-12 09:56:00 +08:00
await Future.delayed(const Duration(seconds: 1));
final res = await Api.getSaleQuotationGroups();
List<SaleQuotationGroupModel> groups = res.data!.items
.map((e) => SaleQuotationGroupModel.fromJson(e))
.toList();
List<SaleQuotationModel> newList = groups
.map((e) => SaleQuotationModel(name: e.name!, items: RxList([])))
.toList();
// List<SaleQuotationModel> newList = [
// SaleQuotationModel(name: '中间过渡架电控部分', items: RxList([])),
// SaleQuotationModel(name: '端头架电控部分', items: RxList([])),
// SaleQuotationModel(name: '主阀部分', items: RxList([])),
// SaleQuotationModel(name: '自动反冲洗过滤器部分', items: RxList([])),
// SaleQuotationModel(name: '位移测量部分', items: RxList([])),
// SaleQuotationModel(name: '压力检测部分', items: RxList([])),
// SaleQuotationModel(name: '煤机定位部分', items: RxList([])),
// SaleQuotationModel(name: '姿态检测部分', items: RxList([])),
// ];
2024-04-02 16:22:21 +08:00
list.assignAll(newList
.where((element) => PinyinHelper.getPinyin(element.name, separator: '')
.contains(searchKey.value))
.toList());
2024-04-02 12:24:08 +08:00
return newList;
}
}
2024-04-12 09:56:00 +08:00
class ItemSearchMoreController
extends BaseSearchMoreController<SaleQuotationItemModel> {
2024-04-02 12:24:08 +08:00
@override
Future<List<SaleQuotationItemModel>> getData({bool isRefresh = false}) async {
2024-04-12 09:56:00 +08:00
try {
final res = await Api.getSaleQuotationComponents();
List<SaleQuotationComponentModel> componets = res.data!.items
.map((e) => SaleQuotationComponentModel.fromJson(e))
.toList();
List<SaleQuotationItemModel> newList = componets
.map((e) => SaleQuotationItemModel(
name: e.name!,
cost: e.unitPrice ?? 0.0,
unit: e.unit?.label ?? '',
remark: e.remark))
.toList();
// List<SaleQuotationItemModel> newList = [
// SaleQuotationItemModel(
// name: '矿用本安型支架控制器', unit: '台', spec: 'ZDYZ-Z', cost: 4700),
// SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
// SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
// SaleQuotationItemModel(name: '矿用本安型隔离耦合器', cost: 1200),
// SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', cost: 600),
// SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-控制器', cost: 400),
// SaleQuotationItemModel(name: '钢丝编织橡胶护套连接器', remark: '控制器-驱动器', cost: 500),
// SaleQuotationItemModel(
// name: '钢丝编织橡胶护套连接器', remark: '控制器-隔离耦合器', cost: 2000),
// SaleQuotationItemModel(name: '矿用本安型支架控制器', cost: 4700),
// SaleQuotationItemModel(name: '矿用本安型电磁阀驱动器', cost: 1200),
// SaleQuotationItemModel(name: '矿用隔爆兼本安型电源', cost: 5700),
// SaleQuotationItemModel(
// name: '电液换向阀(10功能10接口)', remark: '中间过渡架主阀组', cost: 13200),
// SaleQuotationItemModel(
// name: '电液换向阀(20功能20接口)', remark: '端头架主阀组', cost: 26500),
// SaleQuotationItemModel(
// name: '自动反冲洗过滤装置', remark: '流量:900L/min,过滤精度25μm', cost: 2000),
// SaleQuotationItemModel(name: '全自动反冲洗过滤器电缆', remark: '控制器-自动反冲洗'),
// SaleQuotationItemModel(name: '矿用本安型位移传感器'),
// SaleQuotationItemModel(name: '矿用本安型压力传感器'),
// SaleQuotationItemModel(name: '矿用本安型红外发射器'),
// SaleQuotationItemModel(name: '矿用本安型LED信号灯'),
// SaleQuotationItemModel(name: '倾角传感器'),
// SaleQuotationItemModel(name: '各类安装附件'),
// ];
list.assignAll(newList
.where((element) =>
PinyinHelper.getPinyin(element.name, separator: '')
.contains(searchKey.value))
.toList());
return newList;
} catch (e) {
return [];
}
2024-04-02 12:24:08 +08:00
}
2024-04-01 17:35:34 +08:00
}