#include "widget.h"
#include "ui_widget.h"
#include <QtDebug>
#include <QDateTime>
#include <QInputDialog>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
, currentMode(NONE)
, currentBillIndex(-1)
{
ui->setupUi(this);
// 初始化日期时间为当前时间
ui->dateTimeEdit->setDateTime(QDateTime::currentDateTime());
// 设置文本浏览器为只读
ui->textBrowser->setReadOnly(true);
// 初始化操作状态
currentMode = NONE;
currentBillIndex = -1;
}
Widget::~Widget()
{
delete ui;
}
// 商品录入按钮
void Widget::on_pushButton_clicked()
{
currentMode = ADD_ITEM;
ui->textBrowser->setHtml("<h3>商品录入模式</h3>"
"<p>请在输入框中填写商品信息,然后点击<strong>确认</strong>按钮。</p>"
"<p>点击<strong>取消</strong>按钮退出此模式。</p>");
}
// 买入按钮
void Widget::on_pushButton_2_clicked()
{
if (items.empty()) {
QMessageBox::warning(this, "操作失败", "没有商品可购买");
return;
}
currentMode = BUY_ITEMS;
// 生成账单预览
double total = 0;
std::stringstream ss;
ss << "<h3>买入操作 - 账单预览</h3>";
ss << "<table border='1' style='border-collapse: collapse; width: 100%;'>";
ss << "<tr><th>商品名称</th><th>数量</th><th>单价</th><th>小计</th></tr>";
for (const Item& item : items) {
double subtotal = item.quantity * item.price;
total += subtotal;
ss << "<tr><td>" << item.name << "</td><td>" << item.quantity
<< "</td><td>" << std::fixed << std::setprecision(2) << item.price
<< "</td><td>" << std::fixed << std::setprecision(2) << subtotal << "</td></tr>";
}
ss << "<tr><td colspan='3' align='right'><strong>总计:</strong></td><td>"
<< std::fixed << std::setprecision(2) << total << "</td></tr>";
ss << "</table>";
ss << "<p>点击<strong>确认</strong>按钮完成购买,点击<strong>取消</strong>按钮放弃。</p>";
ui->textBrowser->setHtml(QString::fromStdString(ss.str()));
}
// 账单按钮
void Widget::on_pushButton_3_clicked()
{
updateBillDisplay();
}
// 退货按钮
void Widget::on_pushButton_4_clicked()
{
if (bills.empty()) {
QMessageBox::warning(this, "操作失败", "没有可退货的账单");
return;
}
currentMode = RETURN_ITEMS;
showReturnForm();
}
// 显示退货表单
void Widget::showReturnForm()
{
std::stringstream ss;
ss << "<h3>退货操作</h3>";
// 显示账单列表供选择
if (currentMode == RETURN_ITEMS) {
ss << "<p>请选择要退货的账单:</p>";
ss << "<table border='1' style='border-collapse: collapse; width: 100%;'>";
ss << "<tr><th>ID</th><th>日期</th><th>总金额</th><th>类型</th><th>状态</th></tr>";
for (size_t i = 0; i < bills.size(); i++) {
if (!bills[i].isReturn) { // 只显示销售账单
std::string status = "可退货";
if (bills[i].isFullyReturned) {
status = "<span style='color:gray'>已完全退货</span>";
}
ss << "<tr><td><a href='bill:" << i << "'>" << bills[i].id << "</a></td>"
<< "<td>" << bills[i].time << "</td>"
<< "<td>" << std::fixed << std::setprecision(2) << bills[i].total << "</td>"
<< "<td>" << (bills[i].isReturn ? "退货" : "销售") << "</td>"
<< "<td>" << status << "</td></tr>";
}
}
ss << "</table>";
}
// 显示选中的账单详情和退货输入框
else if (currentMode == RETURN_ITEM_SELECTED && currentBillIndex >= 0) {
const Bill& bill = bills[currentBillIndex];
// 检查账单是否已完全退货
if (bill.isFullyReturned) {
ss << "<div style='color:red; background-color:#FFE4E1; padding:10px; border-radius:5px;'>"
<< "<strong>警告:此账单已完全退货,无法再次退货!</strong>"
<< "</div>";
ui->textBrowser->setHtml(QString::fromStdString(ss.str()));
return;
}
ss << "<p><strong>选择的账单: " << bill.id << "</strong></p>";
ss << "<p>请输入要退货的商品信息:</p>";
// 显示账单商品列表
ss << "<table border='1' style='border-collapse: collapse; width: 100%;'>";
ss << "<tr><th>商品名称</th><th>购买数量</th><th>单价</th><th>可退数量</th><th>状态</th></tr>";
for (const Item& item : bill.items) {
std::string status = "可退货";
std::string key = bill.id + "_" + item.name;
// 检查商品是否已完全退货
if (returnedItems.find(key) != returnedItems.end()) {
status = "<span style='color:gray'>已退货</span>";
}
ss << "<tr><td>" << item.name << "</td><td>" << item.quantity
<< "</td><td>" << std::fixed << std::setprecision(2) << item.price
<< "</td><td>" << item.quantity << "</td><td>" << status << "</td></tr>";
}
ss << "</table>";
// 退货输入提示
ss << "<p style='margin-top: 15px;'>"
<< "请在下方输入框中填写:<br>"
<< "1. <strong>商品名称</strong>(必须与账单中一致)<br>"
<< "2. <strong>退货数量</strong>(不能超过可退数量)<br>"
<< "3. <strong>商品单价</strong>(需与原单价一致)"
<< "</p>";
// 添加退货记录提示
if (!returnedItems.empty()) {
ss << "<div style='background-color:#F0F8FF; padding:5px; margin-top:10px;'>"
<< "<strong>注意:</strong>已退货的商品标记为<span style='color:gray'>已退货</span>,不能重复退货"
<< "</div>";
}
}
ui->textBrowser->setHtml(QString::fromStdString(ss.str()));
}
// 日结按钮
void Widget::on_pushButton_5_clicked()
{
showDailySummary();
}
// 确认按钮
void Widget::on_pushButton_6_clicked()
{
switch (currentMode) {
case ADD_ITEM: {
QString name = ui->lineEdit->text();
QString quantity = ui->lineEdit_2->text();
QString price = ui->lineEdit_3->text();
if (name.isEmpty() || quantity.isEmpty() || price.isEmpty()) {
QMessageBox::warning(this, "输入错误", "请填写完整的商品信息");
return;
}
bool ok;
int qty = quantity.toInt(&ok);
if (!ok || qty <= 0) {
QMessageBox::warning(this, "输入错误", "数量必须是正整数");
return;
}
double prc = price.toDouble(&ok);
if (!ok || prc <= 0) {
QMessageBox::warning(this, "输入错误", "单价必须是正数");
return;
}
Item newItem = {name.toStdString(), qty, prc};
items.push_back(newItem);
QMessageBox::information(this, "成功", "商品已添加到列表");
updateItemDisplay();
clearInputFields();
break;
}
case BUY_ITEMS: {
// 生成账单ID和时间
QString id = QString::number(bills.size() + 1);
QString time = ui->dateTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss");
// 计算总金额
double total = 0;
for (const Item& item : items) {
total += item.quantity * item.price;
}
// 创建新账单
Bill newBill = {id.toStdString(), time.toStdString(), items, total, false};
bills.push_back(newBill);
// 清空购物车
items.clear();
QMessageBox::information(this, "交易成功",
QString("账单创建成功!\n账单号: %1\n总金额: %2").arg(id).arg(total, 0, 'f', 2));
updateItemDisplay();
updateBillDisplay();
currentMode = NONE;
break;
}
case RETURN_ITEMS: {
if (currentBillIndex < 0 || currentBillIndex >= static_cast<int>(bills.size())) {
QMessageBox::warning(this, "操作失败", "请先选择要退货的账单");
return;
}
Bill& bill = bills[currentBillIndex];
// 创建退货账单
QString returnId = QString::number(bills.size() + 1);
QString time = ui->dateTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss");
// 简化处理:全额退货
Bill returnBill = {returnId.toStdString(), time.toStdString(),
bill.items, bill.total, true};
bills.push_back(returnBill);
QMessageBox::information(this, "退货成功",
QString("退货完成!\n退货单号: %1\n退款金额: %2").arg(returnId).arg(bill.total, 0, 'f', 2));
updateBillDisplay();
currentMode = NONE;
currentBillIndex = -1;
break;
}
case RETURN_ITEM_SELECTED: {
if (currentBillIndex < 0 || currentBillIndex >= static_cast<int>(bills.size())) {
QMessageBox::warning(this, "操作失败", "请先选择要退货的账单");
return;
}
Bill& originalBill = bills[currentBillIndex];
// 检查账单是否已完全退货
if (originalBill.isFullyReturned) {
QMessageBox::warning(this, "操作失败",
QString("账单 %1 已完全退货,无法再次退货!")
.arg(QString::fromStdString(originalBill.id)));
return;
}
// 获取退货商品信息
QString itemName = ui->lineEdit->text();
QString returnQtyStr = ui->lineEdit_2->text();
QString itemPriceStr = ui->lineEdit_3->text();
if (itemName.isEmpty() || returnQtyStr.isEmpty() || itemPriceStr.isEmpty()) {
QMessageBox::warning(this, "输入错误", "请填写完整的退货信息");
return;
}
bool ok;
int returnQty = returnQtyStr.toInt(&ok);
if (!ok || returnQty <= 0) {
QMessageBox::warning(this, "输入错误", "退货数量必须是正整数");
return;
}
double itemPrice = itemPriceStr.toDouble(&ok);
if (!ok || itemPrice <= 0) {
QMessageBox::warning(this, "输入错误", "商品单价必须是正数");
return;
}
// 在原始账单中查找该商品
auto itemIt = std::find_if(originalBill.items.begin(), originalBill.items.end(),
[&](const Item& item) {
return item.name == itemName.toStdString();
});
if (itemIt == originalBill.items.end()) {
QMessageBox::warning(this, "商品不存在",
QString("在账单 %1 中找不到商品 '%2'")
.arg(QString::fromStdString(originalBill.id), itemName));
return;
}
// 检查商品是否已完全退货
std::string itemKey = originalBill.id + "_" + itemIt->name;
if (returnedItems.find(itemKey) != returnedItems.end()) {
QMessageBox::warning(this, "商品已退货",
QString("商品 '%1' 在此账单中已完全退货,无法再次退货!")
.arg(itemName));
return;
}
// 验证退货数量
if (returnQty > itemIt->quantity) {
QMessageBox::critical(this, "退货数量超出",
QString("<b>退货数量超过购买数量!</b><br><br>"
"商品: %1<br>"
"购买数量: %2<br>"
"退货数量: %3<br>"
"超出数量: %4")
.arg(itemName)
.arg(itemIt->quantity)
.arg(returnQty)
.arg(returnQty - itemIt->quantity));
return;
}
// 验证商品单价
if (std::abs(itemPrice - itemIt->price) > 0.01) {
QMessageBox::warning(this, "单价不匹配",
QString("输入单价与购买单价不一致!<br><br>"
"商品: %1<br>"
"购买单价: %2<br>"
"输入单价: %3")
.arg(itemName)
.arg(itemIt->price, 0, 'f', 2)
.arg(itemPrice, 0, 'f', 2));
return;
}
// 计算退货金额
double returnAmount = returnQty * itemPrice;
// 更新原始账单
itemIt->quantity -= returnQty;
originalBill.total -= returnAmount;
// 检查商品是否完全退货
if (itemIt->quantity <= 0) {
// 标记商品已完全退货
returnedItems.insert(itemKey);
// 从账单中移除商品
originalBill.items.erase(itemIt);
// 检查账单是否完全退货
if (originalBill.items.empty()) {
originalBill.isFullyReturned = true;
}
}
// 创建退货账单
QString returnId = QString::number(bills.size() + 1);
QString time = ui->dateTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss");
// 退货账单只包含退货的商品
std::vector<Item> returnItems;
returnItems.push_back({itemName.toStdString(), returnQty, itemPrice});
Bill returnBill = {returnId.toStdString(), time.toStdString(),
returnItems, returnAmount, true};
bills.push_back(returnBill);
QMessageBox::information(this, "退货成功",
QString("<b>退货成功!</b><br><br>"
"退货单号: %1<br>"
"商品: %2<br>"
"数量: %3<br>"
"单价: %4<br>"
"退款金额: %5")
.arg(returnId)
.arg(itemName)
.arg(returnQty)
.arg(itemPrice, 0, 'f', 2)
.arg(returnAmount, 0, 'f', 2));
updateBillDisplay();
currentMode = RETURN_ITEMS;
showReturnForm();
break;
}
default:
QMessageBox::information(this, "提示", "请先选择要执行的操作");
}
}
// 取消按钮
void Widget::on_pushButton_7_clicked()
{
currentMode = NONE;
currentBillIndex = -1;
ui->textBrowser->clear();
clearInputFields();
}
// 时间变化
void Widget::on_dateTimeEdit_dateChanged(const QDate &date)
{
// 更新显示时使用当前选择的日期
if (currentMode == NONE) {
showDailySummary();
}
}
void Widget::on_textBrowser_anchorClicked(const QUrl &arg1)
{
QString url = arg1.toString();
if (url.startsWith("bill:")) {
QString indexStr = url.mid(5);
bool ok;
int index = indexStr.toInt(&ok);
if (ok && index >= 0 && index < static_cast<int>(bills.size())) {
currentBillIndex = index;
currentMode = RETURN_ITEM_SELECTED;
showReturnForm();
// 清空输入框
clearInputFields();
}
}
}
// 更新账单显示
void Widget::updateBillDisplay()
{
std::stringstream ss;
ss << "<h3>账单列表</h3>";
if (bills.empty()) {
ss << "<p>没有账单</p>";
} else {
ss << "<table border='1' style='border-collapse: collapse; width: 100%;'>";
ss << "<tr><th>ID</th><th>日期</th><th>总金额</th><th>类型</th><th>状态</th><th>操作</th></tr>";
for (size_t i = 0; i < bills.size(); i++) {
std::string status = "";
if (bills[i].isFullyReturned) {
status = "<span style='color:green'>已完全退货</span>";
} else if (!bills[i].isReturn && bills[i].items.empty()) {
status = "<span style='color:gray'>无商品</span>";
}
ss << "<tr><td>" << bills[i].id << "</td>"
<< "<td>" << bills[i].time << "</td>"
<< "<td>" << std::fixed << std::setprecision(2) << bills[i].total << "</td>"
<< "<td>" << (bills[i].isReturn ? "退货" : "销售") << "</td>"
<< "<td>" << status << "</td>"
<< "<td><a href='bill:" << i << "'>查看详情</a></td></tr>";
}
ss << "</table>";
}
ui->textBrowser->setHtml(QString::fromStdString(ss.str()));
}
// 更新商品显示
void Widget::updateItemDisplay()
{
std::stringstream ss;
ss << "<h3>当前商品列表</h3>";
if (items.empty()) {
ss << "<p>没有商品</p>";
} else {
ss << "<table border='1' style='border-collapse: collapse; width: 100%;'>";
ss << "<tr><th>商品名称</th><th>数量</th><th>单价</th></tr>";
for (const Item& item : items) {
ss << "<tr><td>" << item.name << "</td><td>" << item.quantity
<< "</td><td>" << std::fixed << std::setprecision(2) << item.price << "</td></tr>";
}
ss << "</table>";
}
ui->textBrowser->setHtml(QString::fromStdString(ss.str()));
}
// 显示账单详情
void Widget::showBillDetails(const Bill& bill)
{
std::stringstream ss;
ss << "<h3>账单详情 - " << bill.id << "</h3>";
ss << "<p><strong>日期:</strong> " << bill.time << "</p>";
ss << "<p><strong>类型:</strong> " << (bill.isReturn ? "退货" : "销售") << "</p>";
ss << "<table border='1' style='border-collapse: collapse; width: 100%;'>";
ss << "<tr><th>商品名称</th><th>数量</th><th>单价</th><th>小计</th></tr>";
for (const Item& item : bill.items) {
double subtotal = item.quantity * item.price;
ss << "<tr><td>" << item.name << "</td><td>" << item.quantity
<< "</td><td>" << std::fixed << std::setprecision(2) << item.price
<< "</td><td>" << std::fixed << std::setprecision(2) << subtotal << "</td></tr>";
}
ss << "<tr><td colspan='3' align='right'><strong>总计:</strong></td><td>"
<< std::fixed << std::setprecision(2) << bill.total << "</td></tr>";
ss << "</table>";
if (currentMode == RETURN_ITEMS && !bill.isReturn) {
ss << "<p>点击<strong>确认</strong>按钮完成退货操作。</p>";
}
ui->textBrowser->setHtml(QString::fromStdString(ss.str()));
}
// 显示日结汇总
void Widget::showDailySummary()
{
QDate selectedDate = ui->dateTimeEdit->date();
QString dateStr = selectedDate.toString("yyyy-MM-dd");
double totalIncome = 0;
double totalReturn = 0;
for (const Bill& bill : bills) {
QString billDate = QString::fromStdString(bill.time).split(" ")[0];
if (billDate == dateStr) {
if (!bill.isReturn) {
totalIncome += bill.total;
} else {
totalReturn += bill.total;
}
}
}
double netIncome = totalIncome - totalReturn;
std::stringstream ss;
ss << "<h3>日结汇总 - " << dateStr.toStdString() << "</h3>";
ss << "<table border='1' style='border-collapse: collapse; width: 100%;'>";
ss << "<tr><th>项目</th><th>金额</th></tr>";
ss << "<tr><td>总收入</td><td>" << std::fixed << std::setprecision(2) << totalIncome << "</td></tr>";
ss << "<tr><td>总退货</td><td>" << std::fixed << std::setprecision(2) << totalReturn << "</td></tr>";
ss << "<tr><td><strong>净收益</strong></td><td><strong>" << std::fixed << std::setprecision(2) << netIncome << "</strong></td></tr>";
ss << "</table>";
ui->textBrowser->setHtml(QString::fromStdString(ss.str()));
}
// 清空输入框
void Widget::clearInputFields()
{
ui->lineEdit->clear();
ui->lineEdit_2->clear();
ui->lineEdit_3->clear();
}
检查一下日结的逻辑,出现了买完了之后再退货净收益是负值,且销售总额为0的情况,要求净收入等于销售总额减去净利润