今天是个好日子,TaxCore(POS软件)备案指北

POS软件是什么?你好意思吗,还在用老掉牙的Winform。

关于POS

销售终端------POS(point of sale)是一种多功能终端,把它安装在信用卡的特约商户和受理网点中与计算机联成网络,就能实现电子资金自动转账,它具有支持消费、预授权、余额查询和转账等功能,使用起来安全、快捷、可靠。

POS软件大都长这样子:

不好意思了,我没有专门去收集POS软件的UI界面。感兴趣的朋友可以去澳大利亚知名POS软件供应商IDEALPOS的官网去围观:

POS Systems Australia - Software & Hardware - Idealpos

前言

好久以前就想写这篇文章了,一是想要证明我还在程序员队伍里苟延残喘。二是这样的文章我从来就没看见过,写出来万一帮到其他人了呢。今天早上我查看请求的状态时豁然发现已经通过平台的审核了,接下来就是区域的审核:我们公司在FEIJI和SAMOA都有门店。

目前TaxCore提供的税务平台有四个国家(地区)在用:**Samoa ,Fiji,USA, WA.**目前TaxCore上备案通过的也没几家公司,我知道的就只要上面提到的澳大利亚IDEALPOS。

我的软件界面有参考它。在国内税务发票是使用针式打印机,套打发票,而国外的发票要求没这么高,用普通的热敏打印机打印就可以了,因为发票是可以在TaxCore的网站上面看到的。所以不能长期保存也没有问题。下面展示一张财务发票:TaxCore: Invoice Verification - TaxCore,点击一下页面的Journal超链接能看到财政发票的原始样子,大概就是这样子

对于纳税人来说可以通过扫描发票上的二维码进行在线查看。对于商户来说,也可以进入到自己所在区域的税所的门户网站后台查看每一张发票,这是TaxCore的全球的沙盒环境的门户:TaxCore | Log off 。财政发票的从开始到结束部分是不运行有任何改动的。但商家可以在头尾加一点自己的东西比如:

TaxCore的审核是我见过最认真细致的,对发票的要求细致得很。我们软件来来回回经过十几轮审核修好终于才通过了。下面展示TaxCore对发票的要求:

乍一看,这基本上是不可能完成的任务吧,全英文的在线文档,没有人带,还好官方有个工单系统,有问题可以在上面提问,会得到很及时的很友好的回复(由于一般有时区的差异,工作时间不一样不要要求马上有答复)。关于官方的帮助文档的地址请点击这里:TaxCore - Help Viewer (revenue.gov.ws)

指北的正文

复制代码
<!DOCTYPE html>
<html>
<head>
    <title>Online POS</title>
    <meta charset="utf-8">
</head>
<body>
    <h1>Online POS</h1>
    <label for="invoiceRequest">Invoice request json</label>
    <textarea id="invoiceRequest" cols="100" rows="30" style="display:block"></textarea>
    <label for="taxcore_sign_element">Send Invoice Request:</label>
    <!--TaxCore HTML element-->
    <button id="taxcore_sign_element"
                data-taxcore-vsdc-url=" https://vsdc.sandbox.taxcore.online/"
                data-taxcore-input-id="invoiceRequest"
                data-taxcore-output-id="results"
                data-taxcore-invoice-request=""
                data-taxcore-debug="true"
                data-taxcore-signed-invoice-response="">Sign Invoice</button>
    <label for="results">Received Signed Invoice:</label>    
    <textarea readonly id="results" cols="100" rows="30"></textarea>
    <!-- TAXCORE.JS -->
    <script src=" https://vsdc.sandbox.taxcore.online/onlinepos/v1/taxcore.min.js"></script>    
    <!-- Custom script at Online POS -->
    <script>        
        document.getElementById("invoiceRequest").innerHTML = 
                JSON.stringify(CreateExampleInvoiceRequest(), undefined, 4);        
        
document.getElementById("taxcore_sign_element").dataset.taxcoreInvoiceRequest = 
        JSON.stringify(CreateExampleInvoiceRequest());
        
// Listen to messages from TaxCore
        window.onmessage = function (e) {            
            console.log(e.data);
        }

        function CreateExampleInvoiceRequest() {
            var invoiceRequest = {
                "DateAndTimeOfIssue": "2017-08-31T13:28:02.433Z",
                "Cashier": "John",
                "BD": null,
                "BuyerCostCenterId": null,
                "IT": "Normal",
                "TT": "Sale",
                "PaymentType": "Card",
                "InvoiceNumber": "31082017-2",
                "ReferentDocumentNumber": null,
                "PAC": null,
                "Options": {
                    "OmitTextualRepresentation": 0,
                    "OmitQRCodeGen": 0
                },
                "Items": [
                    {
                        "GTIN": null,
                        "Name": "Book",
                        "Quantity": 1,
                        "Labels": [
                            "A"
                        ],
                        "TotalAmount": 50
                    }
                ]
            };

            return invoiceRequest;
        }
    </script>
</body>
</html>

在国内是不存在网络问题的,所以官方提供了HTML在线POS的示例代码。那么我认为有必要提一下为什么最终我选择了用Winform来做这个客户端程序。第一我有多年的CS程序开发经验,在这里3G和4G都满如蜗牛一样的网络条件下我首先想到的是自动实现离线和在线模式切换,先把交易数据存在本地的Sqlite中,一旦网络可用才上传到服务器,而且针对离线交易不能打印在线的财政发票的情况下TaxCore提供了半连接模式可以使用智能卡存储交易数据,一旦网络恢复则从卡里读取数据再上传,提供了一种本地审核的方式,就是离线可以打印财政发票。由于我对WPF等技术不太熟练,综合考虑于是就使用了WinForm来开发客户端程序。下面贴一点WinForm发起发票请求的代码:

复制代码
 /// <summary>
 /// 发起联网发票请求
 /// </summary>
 /// <param name="bill">订单</param>
 public void RequestNormalInvoice(OrderTableEntity bill)
 {
     #region 构建请求json
     TaxCoreBobyRequest request = new TaxCoreBobyRequest();
     request.Cashier = bill.casher;
     if (CurrentMemberObj != null)
         request.BD = string.IsNullOrEmpty(CurrentMemberObj.Buyer_Tin) == true ? null : CurrentMemberObj.Buyer_Tin;
     else
         request.BD = null;
     request.BuyerCostCenterId = null;
     request.IT = "Normal";
     request.TT = "Sale";
     request.InvoiceNumber = bill.orderNo;
     request.ReferentDocumentNumber = null;
     request.PAC = $"{ConfigObj.PAC}";
     var s1 = new BaseRepository<BillPayDetailEntityTable>();
     var s2 = new BaseRepository<BillConsumeDetailEntityTable>();
     var s3 = new BaseRepository<Payment_method_tableEntity>();
     var list1 = s1.GetList(p => p.order_No == bill.orderNo);
     var list2 = s2.GetList(p => p.order_No == bill.orderNo);
     List<GoodsItem> items = new List<GoodsItem>();
     var goodsSvr = new BaseRepository<GoodsTableEntity>();
     #region 商品明细
     foreach (var item in list2)
     {
         var goods = goodsSvr.GetFirst(g => g.goodsNo == item.goods_No);
         items.Add(new GoodsItem
         {
             GTIN = null,
             Name = item.goods_Name,
             Quantity = item.qty,
             UnitPrice = item.sale_price - item.discountAmount,
             TotalAmount = item.qty * (item.sale_price - item.discountAmount),
             Labels = new string[]
             {
                 goods == null ? ConfigObj.MISCELLANEOUS_Rate_Label : goods.taxLabels
             }
         });
     }
     #endregion
     request.Items = items;
     var pInfo = s3.GetList(p => p.pm_Id == list1.FirstOrDefault().payment_method).FirstOrDefault();
     if (pInfo.TaxcoreName != pInfo.pm_Name)
         request.PaymentType = pInfo.TaxcoreName;
     else
         request.PaymentType = pInfo.pm_Name;
     #endregion 
     string invoiceRequest = JsonConvert.SerializeObject(request);
     var httpContent = new StringContent(invoiceRequest, Encoding.UTF8, "application/json");
     HttpClient client;
     WebRequestHandler handler;
     GetClientAndHandler(out handler, out client);
     var response = client.PostAsync($"api/Sign/SignInvoice", httpContent).Result;
     if (response.StatusCode == HttpStatusCode.OK)
     {
         var svr = new BaseRepository<OrderTableEntity>();
         bool b = invoiceRequest.Contains("'");
         if (b)
             invoiceRequest = invoiceRequest.Replace("'", " ");
         string cmd = $"update orders set fiscalInvoiceRequest='{invoiceRequest}' where orderNo='{bill.orderNo}'";
         svr.Context.Ado.ExecuteCommand(cmd);
         var jsonString = response.Content.ReadAsStringAsync();
         jsonString.Wait();
         var invoiceResponse = jsonString.Result;
         var responseObj = JsonConvert.DeserializeObject<TaxCoreResponse>(invoiceResponse);
         orderTableEntity = bill;
         PrintNormalInvoice(responseObj);
         responseObj.VerificationQRCode = "";
         responseObj.ID = "";
         responseObj.S = "";
         responseObj.Journal = "";
         string cmd_sql = $"update orders set UploadDateTime=null,fiscalInvoiceResponse='{JsonConvert.SerializeObject(responseObj)}' where orderNo='{bill.orderNo}'";
         svr.Context.Ado.ExecuteCommand(cmd_sql);
     }
     else
     {
         MessageTip.ShowError($"Failed to request invoice:[{response.StatusCode}]");
         throw new Exception($"Failed to request invoice:[{response.StatusCode}]");
     }
 }



 /// <summary>
 /// 请求地址赋值
 /// </summary>
 /// <param name="handler"></param>
 /// <param name="client"></param>
 public void GetClientAndHandler(out WebRequestHandler handler, out HttpClient client)
 {
     handler = CreateWebRequestHandler();
     client = new HttpClient(handler);
     client.BaseAddress = new Uri($"{ConfigObj.RequstBaseURL}");
     client.DefaultRequestHeaders.Accept.Clear();
 }
 /// <summary>
 /// 请求加入证书
 /// </summary>
 /// <returns></returns>
 private WebRequestHandler CreateWebRequestHandler()
 {
     var handler = new WebRequestHandler();
     var cert = GetClientCertificate();
     handler.ClientCertificateOptions = ClientCertificateOption.Manual;
     handler.ClientCertificates.Add(cert);
     return handler;
 }
 /// <summary>
 /// 读取证书
 /// </summary>
 /// <returns></returns>
 private X509Certificate2 GetClientCertificate()
 {
     X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
     //store.Open(OpenFlags.ReadWrite);
     string certName = $"{ConfigObj.InvoiceCertificateName}";
     store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
     var certificates = store.Certificates;
     foreach (X509Certificate2 cert in certificates)
     {
         if (cert.Subject.Contains(certName))
         {
             return cert;
         }
     }
     return null;
 }

关于打印的代码就有点冗长了,请无视我的复制和粘帖运用得如此炉火纯青。

复制代码
/// <summary>
/// 发票绘制
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PdInvoice_PrintPage(object sender, PrintPageEventArgs e)
{
    string[] journal = currentResponse.Journal.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
    string pos_time = journal[9].Replace("POS Time: ", "");
    if (!string.IsNullOrEmpty(orderTableEntity.member_No))
        pos_time = journal[10].Replace("POS Time: ", "");
    var s1 = new BaseRepository<BillConsumeDetailEntityTable>();
    var s2 = new BaseRepository<BillPayDetailEntityTable>();
    var list1 = s1.GetList(o => o.order_No == orderTableEntity.orderNo);
    var list2 = s2.GetList(o => o.order_No == orderTableEntity.orderNo);
    int right_space = 32;
    e.Graphics.Clear(Color.White);
    Rectangle rect = new Rectangle(0, 0, 300, 360);
    #region StringFormat
    StringFormat sf_center = new StringFormat();
    sf_center.Alignment = StringAlignment.Center;
    sf_center.LineAlignment = StringAlignment.Center;
    StringFormat sf_left = new StringFormat();
    sf_left.Alignment = StringAlignment.Near;
    sf_left.LineAlignment = StringAlignment.Center;
    StringFormat sf_right = new StringFormat();
    sf_right.Alignment = StringAlignment.Far;
    sf_right.LineAlignment = StringAlignment.Center;
    #endregion
    Font tipsFont = new Font("微软雅黑", 9F, FontStyle.Bold);
    Font txtFont = new Font("微软雅黑", 9F);
    e.Graphics.DrawString($"{merchantStoreEntity.name}", tipsFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_center);
    rect.Y += 20;
    e.Graphics.DrawString($"===========================================", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    rect.Y += 20;
    e.Graphics.DrawString($"PH#:{merchantStoreEntity.phone} MOB#:{merchantStoreEntity.mobile}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_center);
    if (!string.IsNullOrEmpty(merchantStoreEntity.store_advertising))
    {
        rect.Y += 20;
        e.Graphics.DrawString($"{merchantStoreEntity.store_advertising}", tipsFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_center);
    }
    rect.Y += 20;
    e.Graphics.DrawString($"========= FISCAL INVOICE ===============", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    rect.Y += 20;
    e.Graphics.DrawString($"TIN:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    e.Graphics.DrawString(currentResponse.TIN, txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    e.Graphics.DrawString($"Company:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    e.Graphics.DrawString($"{currentResponse.BusinessName}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    e.Graphics.DrawString($"Store:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    e.Graphics.DrawString($"{merchantStoreEntity.name}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    e.Graphics.DrawString($"Address:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    e.Graphics.DrawString($"{currentResponse.Address}", txtFont, Brushes.Black, new Rectangle(42, rect.Y, rect.Width - 76, 40), sf_right);
    rect.Y += 40;
    e.Graphics.DrawString($"District:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    e.Graphics.DrawString($"{currentResponse.District}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    e.Graphics.DrawString($"Cashier TIN:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    e.Graphics.DrawString($"{CashierFlowContext.Instance.CurrentEmployee.employeeName}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    e.Graphics.DrawString($"POS Number:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    e.Graphics.DrawString($"{orderTableEntity.orderNo}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    e.Graphics.DrawString($"POS Time:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    e.Graphics.DrawString(pos_time.Trim(), txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    if (!string.IsNullOrEmpty(orderTableEntity.member_No))
    {
        rect.Y += 20;
        e.Graphics.DrawString($"Buyer TIN:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
        e.Graphics.DrawString($"{orderTableEntity.member_No}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    }
    rect.Y += 20;
    e.Graphics.DrawString($"-----------------NORMAL SALE--------------------", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    rect.Y += 20;
    e.Graphics.DrawString($"Items", txtFont, Brushes.Black, new Rectangle(4, rect.Y, rect.Width, 20), sf_center);
    rect.Y += 20;
    e.Graphics.DrawString($"===========================================", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    rect.Y += 18;
    e.Graphics.DrawString($"Name        Price             Qty.               Total    ", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    rect.Y += 16;
    var goodsSvr = new BaseRepository<GoodsTableEntity>();
    foreach (var detailRow in list1)
    {
        var goods = goodsSvr.GetFirst(g => g.goodsNo == detailRow.goods_No);
        string label = goods == null ? ConfigObj.MISCELLANEOUS_Rate_Label : goods.taxLabels;
        e.Graphics.DrawString($"{detailRow.goods_Name} ({label})", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
        e.Graphics.DrawString($"{detailRow.sale_price.ToString("C2").Substring(1)}", txtFont, Brushes.Black, new Rectangle(rect.Width / 2 - 86, rect.Y + 16, rect.Width, 20), sf_left);
        e.Graphics.DrawString($"{detailRow.qty}", txtFont, Brushes.Black, new Rectangle(rect.Width / 2 - 8, rect.Y + 16, rect.Width, 20), sf_left);
        e.Graphics.DrawString($"{detailRow.amount.ToString("C2").Substring(1)}", txtFont, Brushes.Black, new Rectangle(0, rect.Y + 16, rect.Width - right_space - 8, 20), sf_right);
        rect.Y += 34;
    }
    e.Graphics.DrawString($"Total Purchase:", tipsFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    e.Graphics.DrawString($"{orderTableEntity.payAmount.ToString("C2").Substring(1)}", tipsFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    var service = new BaseRepository<Payment_method_tableEntity>();
    var p = service.GetFirst(pm => pm.pm_Id == list2.FirstOrDefault().payment_method);
    e.Graphics.DrawString($"Payment Method:", tipsFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    if (p.TaxcoreName != p.pm_Name)
        e.Graphics.DrawString(p.TaxcoreName, tipsFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    else
        e.Graphics.DrawString($"{p.pm_Name}", tipsFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    e.Graphics.DrawString($"===========================================", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    rect.Y += 18;
    e.Graphics.DrawString($"Label      Name         Rate ", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    e.Graphics.DrawString(" Tax ", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    foreach (var rate in currentResponse.TaxItems)
    {
        e.Graphics.DrawString($"{rate.Label}            {rate.CategoryName}           {rate.Rate}%  ", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
        e.Graphics.DrawString($"{rate.Amount.ToString("C2").Substring(1)} ", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
        rect.Y += 20;
    }
    e.Graphics.DrawString($"--------------------------------------------------", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    rect.Y += 20;
    e.Graphics.DrawString($"Total Tax:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    e.Graphics.DrawString($"{currentResponse.TaxItems.Sum(t => t.Amount).ToString("C2").Substring(1)}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    e.Graphics.DrawString($"===========================================", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    rect.Y += 20;
    e.Graphics.DrawString($"SDC Time:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    string dt = Convert.ToDateTime(currentResponse.DT).ToString("dd/MM/yyyy HH:mm:ss");
    if (ConfigObj.RequstBaseURL.Contains("sandbox"))
    {
        dt = Convert.ToDateTime(currentResponse.DT.AddHours(-12)).ToString("dd/MM/yyyy HH:mm:ss");//.ToString("dd/MM/yyyy HH:mm:ss");
    }
    e.Graphics.DrawString($"{dt}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    e.Graphics.DrawString($"SDC Invoice No: ", new Font("微软雅黑", 8.2F), Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_left);
    e.Graphics.DrawString($"{currentResponse.IN}", new Font("微软雅黑", 8.2F), Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    e.Graphics.DrawString($"Invoice Counter: ", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_left);
    e.Graphics.DrawString($"{currentResponse.IC}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    rect.Y += 20;
    e.Graphics.DrawString($"===========================================", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    rect.Y += 20;
    byte[] bytes = Convert.FromBase64String(currentResponse.VerificationQRCode);
    MemoryStream memStream = new MemoryStream(bytes);
    Image mImage = Image.FromStream(memStream);
    e.Graphics.DrawImage(mImage, new Rectangle(8, rect.Y, 266, 266));
    rect.Y += 266;
    e.Graphics.DrawString($"====== END OF FISCAL INVOICE ==========", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    rect.Y += 20;
    e.Graphics.DrawString($"Subtotal ({list1.Count()} items)", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
    e.Graphics.DrawString($"{list1.Sum(o => o.amount).ToString("C2").Substring(1)}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    if (orderTableEntity.payMethodName.Contains("->("))
    {
        rect.Y += 20;
        e.Graphics.DrawString($"===========Aggregated payment============", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
        rect.Y += 20;
        e.Graphics.DrawString($"Payment method:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
        e.Graphics.DrawString($"{orderTableEntity.payMethodName}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
        rect.Y += 20;
        e.Graphics.DrawString($"Payment Amount:", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
        e.Graphics.DrawString($"{orderTableEntity.payAmount.ToString("C2").Substring(1)}", txtFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    }
    if (p.is_cash.ToUpper().Equals("Y") && (list2.Any(o => o.exchangeAmount > 0)))
    {
        rect.Y += 20;
        e.Graphics.DrawString($"Collection:", tipsFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
        e.Graphics.DrawString($"{(orderTableEntity.payAmount + orderTableEntity.payBackAmount).ToString("C2").Substring(1)}", tipsFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
        rect.Y += 20;
        e.Graphics.DrawString($"Change:", tipsFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_left);
        e.Graphics.DrawString($"{orderTableEntity.payBackAmount.ToString("C2").Substring(1)}", tipsFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width - right_space, 20), sf_right);
    }
    if (!string.IsNullOrEmpty(merchantStoreEntity.store_advertising_footer))
    {
        rect.Y += 22;
        e.Graphics.DrawString($"{merchantStoreEntity.store_advertising_footer}", tipsFont, Brushes.Black, new Rectangle(0, rect.Y, rect.Width, 20), sf_center);
    }
    
}

现有软件的一些截图

相关推荐
余生H9 天前
编程语言的前后端分离:可用JavaScript运行时作为后端的语言及与传统编程语言的对比 -Typescript、Nim、Moonbit
开发语言·javascript·typescript·前后端分离
清风絮柳16 天前
27.旅游推荐管理系统(基于springboot和vue)
vue·毕业设计·springboot·旅游·推荐算法·前后端分离·旅游推荐系统
方才coding21 天前
2024最新的开源博客系统:vue3.x+SpringBoot 3.x 前后端分离
spring boot·后端·开源·博客系统·前后端分离·个人博客·vue 3.x
清风絮柳25 天前
20.体育馆使用预约系统(基于springboot和vue的Java项目)
vue·毕业设计·springboot·java项目·前后端分离·体育馆·体育馆使用预约系统
清风絮柳1 个月前
14.社团管理系统(基于springboot和vue)
vue.js·spring boot·后端·毕业设计·java项目·前后端分离·社团系统
清风絮柳1 个月前
13.音乐管理系统(基于SpringBoot + Vue)
vue.js·spring boot·后端·毕业设计·前后端分离·音乐播放系统
一丝晨光2 个月前
Web技术简史、前后端分离、游戏
前端·javascript·css·游戏·unity·前后端分离·cocos
暮毅2 个月前
一、前后端分离及drf的概念
django·pdf·前后端分离
GoppViper2 个月前
互联网前后端分离的开发场景,一般会员和数据权限的判断是放在前端还是后端?
前端·后端·架构·golang·前后端分离
写代码的中青年3 个月前
快速web开发:Vue和FastAPI完美组合
前端·vue.js·python·fastapi·web·前后端分离