diff --git a/PointOfSale/.gitignore b/PointOfSale/.gitignore new file mode 100644 index 0000000..1ee5385 --- /dev/null +++ b/PointOfSale/.gitignore @@ -0,0 +1,362 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd diff --git a/PointOfSale/EpsonPrinterLinux/Controllers/PosPrinterController.cs b/PointOfSale/EpsonPrinterLinux/Controllers/PosPrinterController.cs new file mode 100644 index 0000000..b1606e8 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Controllers/PosPrinterController.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Model; +using EpsonPrinter.Services; + +namespace EpsonPrinter.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class PosPrinterController : ControllerBase + { + /// + /// PrintReceipt receipt, so far only for all Epson Thermal Printers + /// + /// PrintStyle is: + /// None + /// FontB + /// Bold + /// DoubleHeight + /// DoubleWidth + /// Underline + /// There is support for any combination of PrintStyles + /// ---------------------------------------- + /// + /// + /// + [HttpPost] + [Route("Receipt")] + public void PrintReceipt([FromServices] EpsonPrintService epsonPrint, ReceiptModel receiptModel) + { + epsonPrint.PrintReceipt(receiptModel); + } + + [HttpPost] + [Route("SaleOfDay")] + public void PrintSaleOfDay([FromServices] EpsonPrintService epsonPrint, SaleOfDayModel saleOfDayModel) + { + epsonPrint.PrintSaleOfDay(saleOfDayModel); + } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Controllers/ValuesController.cs b/PointOfSale/EpsonPrinterLinux/Controllers/ValuesController.cs new file mode 100644 index 0000000..557efdc --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Controllers/ValuesController.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace EpsonPrinter.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class ValuesController : ControllerBase + { + [HttpGet] + [Route("HelloWorld")] + public string Get() + { + return "HelloWorld"; + } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Enums/AlignmentEnum.cs b/PointOfSale/EpsonPrinterLinux/Enums/AlignmentEnum.cs new file mode 100644 index 0000000..4efe3a3 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Enums/AlignmentEnum.cs @@ -0,0 +1,9 @@ +namespace EpsonPrinter.Enums +{ + public enum AlignmentEnum + { + Left, + Center, + Right + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Enums/PrintStylesEnum.cs b/PointOfSale/EpsonPrinterLinux/Enums/PrintStylesEnum.cs new file mode 100644 index 0000000..597669b --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Enums/PrintStylesEnum.cs @@ -0,0 +1,12 @@ +namespace EpsonPrinter.Enums +{ + public enum PrintStylesEnum + { + None, + FontB, + Bold, + DoubleHeight, + DoubleWidth, + UnderLine + } +} \ No newline at end of file diff --git a/PointOfSale/EpsonPrinterLinux/EpsonPrinter.csproj b/PointOfSale/EpsonPrinterLinux/EpsonPrinter.csproj new file mode 100644 index 0000000..e4339f7 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/EpsonPrinter.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + Linux + ..\Pos + enable + enable + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PointOfSale/EpsonPrinterLinux/Model/BodyModel.cs b/PointOfSale/EpsonPrinterLinux/Model/BodyModel.cs new file mode 100644 index 0000000..7e89768 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Model/BodyModel.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace EpsonPrinter.Model +{ + public class BodyModel + { + public List Products { get; set; } + public decimal TotalPrice { get; set; } + public decimal TotalVat { get; set; } + public int ReceiptNumber { get; set; } + public string ReceiptTime { get; set; } + public string Staff { get; set; } + + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Model/BodyProductModel.cs b/PointOfSale/EpsonPrinterLinux/Model/BodyProductModel.cs new file mode 100644 index 0000000..00d72aa --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Model/BodyProductModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace EpsonPrinter.Model +{ + public class BodyProductModel + { + public string Product { get; set; } + public int NoOfProduct { get; set; } + public decimal Price { get; set; } + public decimal TotalPrice { get; set; } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Model/FooterModel.cs b/PointOfSale/EpsonPrinterLinux/Model/FooterModel.cs new file mode 100644 index 0000000..d943e1c --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Model/FooterModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Enums; + +namespace EpsonPrinter.Model +{ + public class FooterModel + { + public string Value { get; set; } + public PrintStyleModel PrintStyles { get; set; } + public AlignmentEnum TextAlignment { get; set; } + public int FeedingLines { get; set; } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Model/HeaderModel.cs b/PointOfSale/EpsonPrinterLinux/Model/HeaderModel.cs new file mode 100644 index 0000000..a9ced21 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Model/HeaderModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Enums; + +namespace EpsonPrinter.Model +{ + public class HeaderModel + { + public string Value { get; set; } + public PrintStyleModel PrintStyles { get; set; } + public AlignmentEnum TextAlignment { get; set; } + public int FeedingLines { get; set; } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Model/PrintStyleModel.cs b/PointOfSale/EpsonPrinterLinux/Model/PrintStyleModel.cs new file mode 100644 index 0000000..853dc5c --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Model/PrintStyleModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace EpsonPrinter.Model +{ + public class PrintStyleModel + { + public bool FontB { get; set; } + public bool Bold { get; set; } + public bool DoubleHeight { get; set; } + public bool DoubleWidth { get; set; } + public bool Underline { get; set; } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Model/ReceiptModel.cs b/PointOfSale/EpsonPrinterLinux/Model/ReceiptModel.cs new file mode 100644 index 0000000..04f486b --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Model/ReceiptModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace EpsonPrinter.Model +{ + public class ReceiptModel + { + public string LogoBase64 { get; set; } + public List Header { get; set; } + public BodyModel BodyModel { get; set; } + public List Footer { get; set; } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Model/SaleOfDayDetail.cs b/PointOfSale/EpsonPrinterLinux/Model/SaleOfDayDetail.cs new file mode 100644 index 0000000..f0b67c1 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Model/SaleOfDayDetail.cs @@ -0,0 +1,10 @@ +using System; + +namespace EpsonPrinter.Model +{ + public class SaleOfDayDetail + { + public string Category { get; set; } + public decimal TotalSale { get; set; } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Model/SaleOfDayModel.cs b/PointOfSale/EpsonPrinterLinux/Model/SaleOfDayModel.cs new file mode 100644 index 0000000..c50f634 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Model/SaleOfDayModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace EpsonPrinter.Model +{ + public class SaleOfDayModel + { + public DateTime Date { get; set; } + public int TotalCustomers { get; set; } + public Decimal TotalSale { get; set; } + public List SaleOfDayDetail { get; set; } = new(); + + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Program.cs b/PointOfSale/EpsonPrinterLinux/Program.cs new file mode 100644 index 0000000..c554603 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Program.cs @@ -0,0 +1,23 @@ +using EpsonPrinter.Services; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +var builder = WebApplication.CreateBuilder(args); + +// Services +builder.Services.AddControllers(); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +builder.Services.AddScoped(); + +var app = builder.Build(); + +// Swagger (altid aktiv – vi er på en Pi, ikke i produktion) +app.UseSwagger(); +app.UseSwaggerUI(); + +// Routing +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/PointOfSale/EpsonPrinterLinux/Properties/launchSettings.json b/PointOfSale/EpsonPrinterLinux/Properties/launchSettings.json new file mode 100644 index 0000000..896e040 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:23653", + "sslPort": 44334 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "EpsonPrinter": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/README_LINUX_USB.txt b/PointOfSale/EpsonPrinterLinux/README_LINUX_USB.txt new file mode 100644 index 0000000..b0949b8 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/README_LINUX_USB.txt @@ -0,0 +1,18 @@ +Linux USB version of the EpsonPrinter API. + +What changed: +- Keeps the incoming JSON models for /api/PosPrinter/Receipt and /api/PosPrinter/SaleOfDay +- Replaces COM/SerialPrinter transport with raw writes to Linux USB printer device +- Uses /dev/usb/lp0 by default from appsettings.json +- Builds a standard receipt when parts of the payload are missing +- Pulls fallback values from appsettings.json under PrintSettings:Defaults + +Typical run: + dotnet restore + dotnet run + +Typical endpoint: + POST /api/PosPrinter/Receipt + +Important setting: + PrintSettings:DevicePath = /dev/usb/lp0 diff --git a/PointOfSale/EpsonPrinterLinux/Services/EpsonPrintService.cs b/PointOfSale/EpsonPrinterLinux/Services/EpsonPrintService.cs new file mode 100644 index 0000000..d551364 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Services/EpsonPrintService.cs @@ -0,0 +1,248 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using EpsonPrinter.Model; +using Microsoft.Extensions.Configuration; + +namespace EpsonPrinter.Services +{ + public class EpsonPrintService + { + private readonly IConfiguration _config; + private readonly string _devicePath; + + public EpsonPrintService(IConfiguration config) + { + _config = config; + _devicePath = _config["PrintSettings:DevicePath"] ?? "/dev/usb/lp0"; + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + } + + public void PrintReceipt(ReceiptModel model) + { + ICommandEmitter e = new EPSON(); + InitAndWrite(e); + + List printBytes = new List(); + PrintHeader printHeader = new PrintHeader(e); + PrintBody printBody = new PrintBody(e, _config); + PrintFooter printFooter = new PrintFooter(e); + + var header = (model.Header != null && model.Header.Any()) ? model.Header : BuildDefaultHeader(); + var body = model.BodyModel ?? BuildDefaultBody(); + if (string.IsNullOrWhiteSpace(body.Staff)) + { + body.Staff = _config["PrintSettings:Defaults:Staff"] ?? "Ukendt"; + } + if (string.IsNullOrWhiteSpace(body.ReceiptTime)) + { + body.ReceiptTime = DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss"); + } + if (body.ReceiptNumber <= 0) + { + body.ReceiptNumber = Math.Abs(Environment.TickCount % 1000000); + } + body.Products ??= new List(); + if (!body.Products.Any()) + { + body.Products.Add(new BodyProductModel + { + Product = "Standard vare", + NoOfProduct = 1, + Price = 99.00m, + TotalPrice = 99.00m + }); + } + if (body.TotalPrice <= 0) + { + body.TotalPrice = body.Products.Sum(x => x.TotalPrice <= 0 ? x.Price * x.NoOfProduct : x.TotalPrice); + } + if (body.TotalVat <= 0) + { + body.TotalVat = Math.Round(body.TotalPrice * 0.25m, 2); + } + + var footer = (model.Footer != null && model.Footer.Any()) ? model.Footer : BuildDefaultFooter(); + + printBytes.AddRange(printHeader.Print(header)); + printBytes.AddRange(printBody.Print(body)); + printBytes.AddRange(printFooter.Print(footer)); + + byte[][] array = printBytes.SelectMany(c => c).ToArray(); + Write(array); + Write(e.FeedLines(5)); + Write(e.FullCut()); + } + + public void PrintSaleOfDay(SaleOfDayModel model) + { + ICommandEmitter e = new EPSON(); + InitAndWrite(e); + PrintSaleOfDay(model, e); + } + + private void InitAndWrite(ICommandEmitter e) + { + Write(e.Initialize()); + Write(e.Enable()); + Write(e.CodePage(CodePage.ISO8859_15_LATIN9)); + Write(e.EnableAutomaticStatusBack()); + } + + private void PrintSaleOfDay(SaleOfDayModel model, ICommandEmitter e) + { + List data = new List(); + + byte[][] dateBytes = + { + e.SetStyles(PrintStyle.DoubleWidth), + e.LeftAlign(), + e.PrintLine($"Dato: {model.Date:dd-MM-yyyy}") + }; + data.Add(dateBytes); + + byte[][] seperater = + { + e.SetStyles(PrintStyle.None), + e.LeftAlign(), + e.PrintLine("-----------------------------------") + }; + data.Add(seperater); + + foreach (SaleOfDayDetail saleOfDayDetail in model.SaleOfDayDetail) + { + string pS = $"{saleOfDayDetail.Category} : {saleOfDayDetail.TotalSale.ToString("#.#0", CultureInfo.InvariantCulture)}"; + byte[][] totalPrice = + { + e.SetStyles(PrintStyle.DoubleWidth), + e.LeftAlign(), + e.PrintLine(pS) + }; + data.Add(totalPrice); + } + + data.Add(seperater); + + string totalSale = $"Total salg: {model.TotalSale.ToString("#.#0", CultureInfo.InvariantCulture)}"; + byte[][] tSBytes = + { + e.SetStyles(PrintStyle.DoubleWidth), + e.LeftAlign(), + e.PrintLine(totalSale) + }; + data.Add(tSBytes); + + string totalCustomer = $"Antal kunder: {model.TotalCustomers}"; + byte[][] tCBytes = + { + e.SetStyles(PrintStyle.DoubleWidth), + e.LeftAlign(), + e.PrintLine(totalCustomer) + }; + data.Add(tCBytes); + + byte[][] array = data.SelectMany(c => c).ToArray(); + Write(array); + Write(e.FeedLines(5)); + Write(e.FullCut()); + } + + private List BuildDefaultHeader() + { + return new List + { + new HeaderModel + { + Value = _config["PrintSettings:Defaults:StoreName"] ?? "TableTop3D", + TextAlignment = Enums.AlignmentEnum.Center, + FeedingLines = 0, + PrintStyles = new PrintStyleModel { Bold = true, DoubleWidth = true, DoubleHeight = true } + }, + new HeaderModel + { + Value = _config["PrintSettings:Defaults:AddressLine1"] ?? string.Empty, + TextAlignment = Enums.AlignmentEnum.Center, + FeedingLines = 0, + PrintStyles = new PrintStyleModel { Bold = true } + }, + new HeaderModel + { + Value = _config["PrintSettings:Defaults:AddressLine2"] ?? string.Empty, + TextAlignment = Enums.AlignmentEnum.Center, + FeedingLines = 0, + PrintStyles = new PrintStyleModel() + }, + new HeaderModel + { + Value = _config["PrintSettings:Defaults:Cvr"] ?? string.Empty, + TextAlignment = Enums.AlignmentEnum.Center, + FeedingLines = 1, + PrintStyles = new PrintStyleModel() + } + }.Where(x => !string.IsNullOrWhiteSpace(x.Value)).ToList(); + } + + private FooterModel CreateFooterLine(string value, bool bold = false) + { + return new FooterModel + { + Value = value, + TextAlignment = Enums.AlignmentEnum.Center, + FeedingLines = 0, + PrintStyles = new PrintStyleModel { Bold = bold } + }; + } + + private List BuildDefaultFooter() + { + var values = new[] + { + _config["PrintSettings:Defaults:FooterLine1"] ?? "Tak for dit køb!", + _config["PrintSettings:Defaults:FooterLine2"] ?? string.Empty, + _config["PrintSettings:Defaults:Phone"] ?? string.Empty + }.Where(x => !string.IsNullOrWhiteSpace(x)).ToList(); + + return values.Select((x, i) => CreateFooterLine(x, i == 0)).ToList(); + } + + private BodyModel BuildDefaultBody() + { + return new BodyModel + { + ReceiptNumber = Math.Abs(Environment.TickCount % 1000000), + ReceiptTime = DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss"), + Staff = _config["PrintSettings:Defaults:Staff"] ?? "Ukendt", + Products = new List + { + new BodyProductModel + { + Product = "Standard vare", + NoOfProduct = 1, + Price = 99.00m, + TotalPrice = 99.00m + } + }, + TotalPrice = 99.00m, + TotalVat = 24.75m + }; + } + + private void Write(byte[] data) + { + using var stream = new FileStream(_devicePath, FileMode.Open, FileAccess.Write, FileShare.ReadWrite); + stream.Write(data, 0, data.Length); + stream.Flush(); + } + + private void Write(byte[][] data) + { + foreach (var segment in data) + { + Write(segment); + } + } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Services/PrintAlignment.cs b/PointOfSale/EpsonPrinterLinux/Services/PrintAlignment.cs new file mode 100644 index 0000000..4a7ad9f --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Services/PrintAlignment.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Input; +using EpsonPrinter.Enums; +using ESCPOS_NET.Emitters; + +namespace EpsonPrinter.Services +{ + public class PrintAlignment + { + public byte[] GetTextAlignment(ICommandEmitter e, AlignmentEnum alignment) + { + if (alignment == AlignmentEnum.Left) + return e.LeftAlign(); + + if (alignment == AlignmentEnum.Right) + return e.RightAlign(); + + return e.CenterAlign(); + } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Services/PrintBody.cs b/PointOfSale/EpsonPrinterLinux/Services/PrintBody.cs new file mode 100644 index 0000000..afb1d26 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Services/PrintBody.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Model; +using ESCPOS_NET.Emitters; +using Microsoft.Extensions.Configuration; + +namespace EpsonPrinter.Services +{ + public class PrintBody + { + private ICommandEmitter _e; + private IConfiguration _config; + + private int productMaxWidth; + + public PrintBody(ICommandEmitter e, IConfiguration config) + { + _e = e; + _config = config; + } + + public List Print(BodyModel model) + { + List data = new List(); + + byte[][] headerModel = + { + _e.SetStyles(PrintStyle.None), + _e.LeftAlign(), + _e.PrintLine($"Dato.........: {model.ReceiptTime}"), + _e.PrintLine($"Ekspedient...: {model.Staff}"), + _e.PrintLine($"Kvittering...: {model.ReceiptNumber}"), + _e.FeedLines(1) + }; + data.Add(headerModel); + + int printWidth = Convert.ToInt32(_config["PrintSettings:PrintWidth"]); + foreach (BodyProductModel product in model.Products) + { + //Size of product is max half of recipt + string totalProductLine = MakeProductNameString(product.Product, printWidth); + totalProductLine += MakeProductNumber(product.NoOfProduct, product.Price); + totalProductLine = MakeTotalProductPrice(totalProductLine, product.TotalPrice,printWidth); + + byte[][] p = + { + _e.PrintLine(totalProductLine) + }; + data.Add(p); + } + + PrintString printString = new PrintString(); + string totalPricePrint = printString.MakePrintString(printWidth, $"Total: ", $"{model.TotalPrice:F} DKK"); + byte[][] totalPrice = + { + _e.FeedLines(1), + _e.SetStyles(PrintStyle.Bold | PrintStyle.DoubleHeight), + _e.RightAlign(), + _e.PrintLine(totalPricePrint) + }; + data.Add(totalPrice); + + byte[][] vat = + { + _e.FeedLines(1), + _e.SetStyles(PrintStyle.None), + _e.LeftAlign(), + _e.PrintLine(MakeVat(printWidth,model.TotalVat)) + }; + data.Add(vat); + + return data; + } + + private string MakeProductNameString(string product, int totalWidth) + { + string productString = product; + productMaxWidth = (totalWidth / 2) - 5; + int productLength = product.Length; + if (productLength < productMaxWidth) + { + + for (int i = productLength; i < productMaxWidth; i++) + { + productString += "."; + } + } + + return productString; + } + + private string MakeProductNumber(int noOfProduct, decimal price) + { + return $"{noOfProduct} á {price:F} DKK :"; + } + + private string MakeTotalProductPrice(string buffer, decimal totalPrice, int printWidth) + { + PrintString printString = new PrintString(); + string p = $"{totalPrice:F} DKK"; + string makePrintString = printString.MakePrintString(printWidth, buffer, p); + return makePrintString; + } + + private string MakeVat(int printWidth, decimal vat) + { + PrintString printString = new PrintString(); + string vatDesc = $"Moms udgør 25% :"; + string v = $"{vat:F} DKK"; + string vatString = printString.MakePrintString(printWidth, vatDesc, v); + return vatString; + } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Services/PrintFooter.cs b/PointOfSale/EpsonPrinterLinux/Services/PrintFooter.cs new file mode 100644 index 0000000..d23e8f4 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Services/PrintFooter.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Model; +using ESCPOS_NET; +using ESCPOS_NET.Emitters; + +namespace EpsonPrinter.Services +{ + public class PrintFooter + { + private ICommandEmitter _e; + + public PrintFooter(ICommandEmitter e) + { + _e = e; + } + + public List Print(List model) + { + List data = new List(); + PrintStyleCombination printStyleCombination = new PrintStyleCombination(); + PrintAlignment printAlignment = new PrintAlignment(); + byte[][] feedLines = + { + _e.FeedLines(1) + }; + data.Add(feedLines); + foreach (FooterModel footerModel in model) + { + + PrintStyle printStyle = printStyleCombination.Combine(footerModel.PrintStyles); + byte[][] bytes = { + _e.SetStyles(PrintStyle.None), + printAlignment.GetTextAlignment(_e,footerModel.TextAlignment), + _e.SetStyles(printStyle), + _e.PrintLine(footerModel.Value), + _e.FeedLines(footerModel.FeedingLines) + }; + data.Add(bytes); + } + + return data; + } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Services/PrintHeader.cs b/PointOfSale/EpsonPrinterLinux/Services/PrintHeader.cs new file mode 100644 index 0000000..eec3fd3 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Services/PrintHeader.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using EpsonPrinter.Model; +using ESCPOS_NET; +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; + +namespace EpsonPrinter.Services +{ + public class PrintHeader + { + private ICommandEmitter _e; + + public PrintHeader(ICommandEmitter e) + { + _e = e; + } + + public List Print(List model) + { + List data = new List(); + PrintStyleCombination printStyleCombination = new PrintStyleCombination(); + PrintAlignment printAlignment = new PrintAlignment(); + foreach (HeaderModel headerModel in model) + { + + PrintStyle printStyle = printStyleCombination.Combine(headerModel.PrintStyles); + byte[][] bytes = { + _e.SetStyles(PrintStyle.None), + printAlignment.GetTextAlignment(_e,headerModel.TextAlignment), + _e.SetStyles(printStyle), + _e.PrintLine(headerModel.Value), + _e.FeedLines(headerModel.FeedingLines) + }; + data.Add(bytes); + } + byte[][] feedLines = + { + _e.FeedLines(1) + }; + data.Add(feedLines); + return data; + } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Services/PrintString.cs b/PointOfSale/EpsonPrinterLinux/Services/PrintString.cs new file mode 100644 index 0000000..fb664a1 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Services/PrintString.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace EpsonPrinter.Services +{ + public class PrintString + { + /// + /// An appropriate interval is converted into the length of + /// the tab about two texts. And make a printing data. + /// + /// + /// The width of the territory which it prints on is converted into the number of + /// characters, and that value is specified. + /// + /// + /// It is necessary as an information for deciding the interval of the text. + /// + /// + /// It is necessary as an information for deciding the interval of the text, too. + /// + /// printing data. + /// + public String MakePrintString(int iLineChars, String strBuf, String strPrice) + { + int iSpaces = 0; + String tab = ""; + try + { + iSpaces = iLineChars - (strBuf.Length + strPrice.Length); + for (int j = 0; j < iSpaces; j++) + { + tab += " "; + } + } + catch (Exception) + { + } + return strBuf + tab + strPrice; + } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/Services/PrintStyleCombination.cs b/PointOfSale/EpsonPrinterLinux/Services/PrintStyleCombination.cs new file mode 100644 index 0000000..bd6adfd --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/Services/PrintStyleCombination.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Enums; +using EpsonPrinter.Model; +using ESCPOS_NET.Emitters; + +namespace EpsonPrinter.Services +{ + public class PrintStyleCombination + { + public PrintStyle Combine(PrintStyleModel printStyleModel) + { + + //Bold + if (printStyleModel.Bold && !printStyleModel.DoubleHeight && !printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.Bold; + + if (printStyleModel.Bold && printStyleModel.DoubleHeight && !printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.Bold | PrintStyle.DoubleHeight; + + if(printStyleModel.Bold && printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.Bold | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth; + + if (printStyleModel.Bold && printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.Bold | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.FontB; + + if (printStyleModel.Bold && printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && printStyleModel.FontB && printStyleModel.Underline) + return PrintStyle.Bold | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.FontB | PrintStyle.Underline; + + //DoubleHeight + if (printStyleModel.DoubleHeight && !printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.DoubleHeight; + + if (printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.DoubleHeight | PrintStyle.DoubleWidth; + + if (printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.FontB; + + if (printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && printStyleModel.FontB && printStyleModel.Underline) + return PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.FontB | PrintStyle.Underline; + + //DoubleWidth + if (printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.DoubleWidth; + + if (printStyleModel.DoubleWidth && printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.DoubleWidth | PrintStyle.FontB; + + if (printStyleModel.DoubleWidth && printStyleModel.FontB && printStyleModel.Underline) + return PrintStyle.DoubleWidth | PrintStyle.FontB | PrintStyle.Underline; + + //FontB + if (printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.FontB; + + if(printStyleModel.FontB && printStyleModel.Underline) + return PrintStyle.FontB | PrintStyle.Underline; + + //Underline + if (printStyleModel.Underline) + return PrintStyle.Underline; + + return PrintStyle.None; + } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/appsettings.Development.json b/PointOfSale/EpsonPrinterLinux/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/appsettings.json b/PointOfSale/EpsonPrinterLinux/appsettings.json new file mode 100644 index 0000000..e0ec02d --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/appsettings.json @@ -0,0 +1,26 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "PrintSettings": { + "DevicePath": "/dev/usb/lp0", + "PrintWidth": 48, + "CodePage": "ISO8859_15_LATIN9", + "Defaults": { + "StoreName": "Blomster Til Alt", + "AddressLine1": "Adelgade 88", + "AddressLine2": "5400 Bogense", + "Cvr": "CVR: 37144436", + "Phone": "Tlf: 41 82 71 66", + "FooterLine1": "Tak for dit køb!", + "FooterLine2": "Vi håber du bliver glad for dine varer.", + "Staff": "Ukendt", + "ReceiptPrefix": "BON" + } + } +} diff --git a/PointOfSale/EpsonPrinterLinux/testdata.json b/PointOfSale/EpsonPrinterLinux/testdata.json new file mode 100644 index 0000000..2d10382 --- /dev/null +++ b/PointOfSale/EpsonPrinterLinux/testdata.json @@ -0,0 +1,73 @@ +{ + "logoBase64": "string", + "header": [ + { + "value": "Frste linie med ", + "printStyles": { + "fontB": false, + "bold": true, + "doubleHeight": false, + "doubleWidth": false, + "underline": false + }, + "textAlignment": 0 + }, + { + "value": "Anden linie", + "printStyles": { + "fontB": false, + "bold": true, + "doubleHeight": false, + "doubleWidth": false, + "underline": false + }, + "textAlignment": 0 + }, + { + "value": "Tredje linie", + "printStyles": { + "fontB": false, + "bold": true, + "doubleHeight": false, + "doubleWidth": false, + "underline": false + }, + "textAlignment": 0 + }, + { + "value": "Fjerde linie", + "printStyles": { + "fontB": false, + "bold": true, + "doubleHeight": false, + "doubleWidth": false, + "underline": false + }, + "textAlignment": 0 + }, + { + "value": "Femte linie", + "printStyles": { + "fontB": false, + "bold": true, + "doubleHeight": false, + "doubleWidth": false, + "underline": false + }, + "textAlignment": 0 + } + ], + "footer": [ + { + "value": "string", + "printStyles": { + "fontB": true, + "bold": true, + "doubleHeight": true, + "doubleWidth": true, + "underline": true + }, + "textAlignment": 0 + } + ] +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/Class1.cs b/PointOfSale/Pos.Api/Pos.Api.Database/Class1.cs new file mode 100644 index 0000000..3783b79 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/Class1.cs @@ -0,0 +1,7 @@ +namespace Pos.Api.Database +{ + public class Class1 + { + + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/Data/PosApiContext.cs b/PointOfSale/Pos.Api/Pos.Api.Database/Data/PosApiContext.cs new file mode 100644 index 0000000..11a03e6 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/Data/PosApiContext.cs @@ -0,0 +1,162 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Pos.Api.Database.Models; + +namespace Pos.Api.Database.Data; + +public partial class PosApiContext : DbContext +{ + public PosApiContext(DbContextOptions options) + : base(options) + { + } + + public virtual DbSet Employees { get; set; } + + public virtual DbSet LastUpdates { get; set; } + + public virtual DbSet Payments { get; set; } + + public virtual DbSet Products { get; set; } + + public virtual DbSet Productgroups { get; set; } + + public virtual DbSet Sales { get; set; } + + public virtual DbSet SaleLines { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .UseCollation("latin1_swedish_ci") + .HasCharSet("latin1"); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + + entity + .ToTable("employee") + .HasCharSet("utf8mb4") + .UseCollation("utf8mb4_general_ci"); + + entity.Property(e => e.Id).HasColumnType("int(11)"); + entity.Property(e => e.IsArchived).HasColumnType("tinyint(4)"); + entity.Property(e => e.Name) + .IsRequired() + .HasMaxLength(50); + }); + + modelBuilder.Entity(entity => + { + entity + .HasNoKey() + .ToTable("LastUpdate"); + + entity.Property(e => e.DateTime).HasColumnType("datetime"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + + entity + .ToTable("payment") + .HasCharSet("armscii8") + .UseCollation("armscii8_bin"); + + entity.Property(e => e.Id).HasColumnType("int(11)"); + entity.Property(e => e.Amount).HasPrecision(20, 6); + entity.Property(e => e.SaleId).HasColumnType("int(11)"); + entity.Property(e => e.Type) + .IsRequired() + .HasColumnType("tinytext"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + + entity + .ToTable("product") + .HasCharSet("utf8mb4") + .UseCollation("utf8mb4_general_ci"); + + entity.HasIndex(e => e.ProductGroupId, "FK_Product_Categories"); + + entity.Property(e => e.Id) + .ValueGeneratedOnAdd() + .HasColumnType("int(11)"); + entity.Property(e => e.Description).HasColumnType("mediumtext"); + entity.Property(e => e.Index).HasColumnType("int(11)"); + entity.Property(e => e.IsArchived).HasColumnType("tinyint(4)"); + entity.Property(e => e.Name) + .IsRequired() + .HasColumnType("tinytext"); + entity.Property(e => e.Price).HasPrecision(10, 2); + entity.Property(e => e.ProductGroupId).HasColumnType("int(11)"); + + entity.HasOne(d => d.IdNavigation).WithOne(p => p.Product) + .HasForeignKey(d => d.Id) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK_Product_ProductGroup"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + + entity + .ToTable("productgroup") + .HasCharSet("utf8mb4") + .UseCollation("utf8mb4_general_ci"); + + entity.Property(e => e.Id).HasColumnType("int(11)"); + entity.Property(e => e.Index).HasColumnType("int(11)"); + entity.Property(e => e.IsArchived).HasColumnType("tinyint(4)"); + entity.Property(e => e.Name) + .IsRequired() + .HasColumnType("tinytext"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + + entity + .ToTable("sale") + .HasCharSet("armscii8") + .UseCollation("armscii8_bin"); + + entity.Property(e => e.Id).HasColumnType("int(11)"); + entity.Property(e => e.EmployeeId).HasColumnType("int(11)"); + entity.Property(e => e.Time).HasColumnType("datetime"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + + entity + .ToTable("sale_line") + .HasCharSet("armscii8") + .UseCollation("armscii8_bin"); + + entity.Property(e => e.Id).HasColumnType("int(11)"); + entity.Property(e => e.Pieces).HasColumnType("smallint(6)"); + entity.Property(e => e.Price).HasPrecision(20, 6); + entity.Property(e => e.Product) + .IsRequired() + .HasColumnType("tinytext"); + entity.Property(e => e.SaleId).HasColumnType("int(11)"); + entity.Property(e => e.Total).HasPrecision(20, 6); + }); + + OnModelCreatingPartial(modelBuilder); + } + + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/Data/PosApiContextExtension.cs b/PointOfSale/Pos.Api/Pos.Api.Database/Data/PosApiContextExtension.cs new file mode 100644 index 0000000..50f2d58 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/Data/PosApiContextExtension.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pos.Api.Database.Data +{ + public partial class PosApiContext + { + + protected override void OnConfiguring( + DbContextOptionsBuilder optionsBuilder) + { + LoadConfig l = new LoadConfig(); + IConfiguration config = l.ByEnvironment(); + string connectionString = config["MySQL"].ToString(); + optionsBuilder + .UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)) + .UseLoggerFactory(LoggerFactory.Create(b => b + .AddFilter(level => level >= LogLevel.Information))) + .EnableSensitiveDataLogging() + .EnableDetailedErrors(); + base.OnConfiguring(optionsBuilder); + } + } +} diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/LoadConfig.cs b/PointOfSale/Pos.Api/Pos.Api.Database/LoadConfig.cs new file mode 100644 index 0000000..e9f19b5 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/LoadConfig.cs @@ -0,0 +1,55 @@ +using Microsoft.Extensions.Configuration; + +namespace Pos.Api.Database +{ + public class LoadConfig + { + private string dir; + public LoadConfig() + { + dir = AppDomain.CurrentDomain.BaseDirectory; + } + public IConfiguration ByEnvironment() + { + //Console.Out.WriteLine($"Json path: {dir}"); +#if DEBUG + + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) + .Build(); + return config; + +#else + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true) + .Build(); + return config; +#endif + //Running in a environment that not is supported in this setup + throw new Exception("HostingEnvironment is not supported! This config setup only supports Development or Production"); + } + + public IConfiguration DebugConfiguration() + { + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) + .Build(); + return config; + } + + public IConfiguration ReleaseConfiguration() + { + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true) + .Build(); + return config; + } + } +} diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/Models/Employee.cs b/PointOfSale/Pos.Api/Pos.Api.Database/Models/Employee.cs new file mode 100644 index 0000000..1937bb5 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/Models/Employee.cs @@ -0,0 +1,15 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; + +namespace Pos.Api.Database.Models; + +public partial class Employee +{ + public int Id { get; set; } + + public string Name { get; set; } + + public sbyte IsArchived { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/Models/LastUpdate.cs b/PointOfSale/Pos.Api/Pos.Api.Database/Models/LastUpdate.cs new file mode 100644 index 0000000..f8efc77 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/Models/LastUpdate.cs @@ -0,0 +1,11 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; + +namespace Pos.Api.Database.Models; + +public partial class LastUpdate +{ + public DateTime? DateTime { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/Models/Payment.cs b/PointOfSale/Pos.Api/Pos.Api.Database/Models/Payment.cs new file mode 100644 index 0000000..459393a --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/Models/Payment.cs @@ -0,0 +1,17 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; + +namespace Pos.Api.Database.Models; + +public partial class Payment +{ + public int Id { get; set; } + + public int SaleId { get; set; } + + public decimal Amount { get; set; } + + public string Type { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/Models/Product.cs b/PointOfSale/Pos.Api/Pos.Api.Database/Models/Product.cs new file mode 100644 index 0000000..974e0ad --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/Models/Product.cs @@ -0,0 +1,25 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; + +namespace Pos.Api.Database.Models; + +public partial class Product +{ + public int Id { get; set; } + + public string Name { get; set; } + + public decimal Price { get; set; } + + public string Description { get; set; } + + public int ProductGroupId { get; set; } + + public sbyte IsArchived { get; set; } + + public int Index { get; set; } + + public virtual Productgroup IdNavigation { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/Models/Productgroup.cs b/PointOfSale/Pos.Api/Pos.Api.Database/Models/Productgroup.cs new file mode 100644 index 0000000..785d8c1 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/Models/Productgroup.cs @@ -0,0 +1,19 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; + +namespace Pos.Api.Database.Models; + +public partial class Productgroup +{ + public int Id { get; set; } + + public string Name { get; set; } + + public sbyte IsArchived { get; set; } + + public int Index { get; set; } + + public virtual Product Product { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/Models/Sale.cs b/PointOfSale/Pos.Api/Pos.Api.Database/Models/Sale.cs new file mode 100644 index 0000000..47e6719 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/Models/Sale.cs @@ -0,0 +1,15 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; + +namespace Pos.Api.Database.Models; + +public partial class Sale +{ + public int Id { get; set; } + + public DateTime Time { get; set; } + + public int EmployeeId { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/Models/SaleLine.cs b/PointOfSale/Pos.Api/Pos.Api.Database/Models/SaleLine.cs new file mode 100644 index 0000000..39be12c --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/Models/SaleLine.cs @@ -0,0 +1,21 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; + +namespace Pos.Api.Database.Models; + +public partial class SaleLine +{ + public int Id { get; set; } + + public int SaleId { get; set; } + + public string Product { get; set; } + + public short Pieces { get; set; } + + public decimal Price { get; set; } + + public decimal Total { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/Pos.Api.Database.csproj b/PointOfSale/Pos.Api/Pos.Api.Database/Pos.Api.Database.csproj new file mode 100644 index 0000000..853b434 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/Pos.Api.Database.csproj @@ -0,0 +1,15 @@ + + + + net7.0 + enable + enable + + + + + + + + + diff --git a/PointOfSale/Pos.Api/Pos.Api.Database/efpt.config.json b/PointOfSale/Pos.Api/Pos.Api.Database/efpt.config.json new file mode 100644 index 0000000..4c89b29 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api.Database/efpt.config.json @@ -0,0 +1,66 @@ +{ + "CodeGenerationMode": 3, + "ContextClassName": "PosApiContext", + "ContextNamespace": null, + "DefaultDacpacSchema": null, + "FilterSchemas": false, + "IncludeConnectionString": false, + "ModelNamespace": null, + "OutputContextPath": "Data", + "OutputPath": "Models", + "PreserveCasingWithRegex": true, + "ProjectRootNamespace": "Pos.Api.Database", + "Schemas": null, + "SelectedHandlebarsLanguage": 0, + "SelectedToBeGenerated": 0, + "Tables": [ + { + "Name": "employee", + "ObjectType": 0 + }, + { + "Name": "LastUpdate", + "ObjectType": 0 + }, + { + "Name": "payment", + "ObjectType": 0 + }, + { + "Name": "product", + "ObjectType": 0 + }, + { + "Name": "productgroup", + "ObjectType": 0 + }, + { + "Name": "sale", + "ObjectType": 0 + }, + { + "Name": "sale_line", + "ObjectType": 0 + } + ], + "UiHint": "simply.com", + "UncountableWords": null, + "UseBoolPropertiesWithoutDefaultSql": false, + "UseDatabaseNames": false, + "UseDateOnlyTimeOnly": false, + "UseDbContextSplitting": false, + "UseFluentApiOnly": true, + "UseHandleBars": false, + "UseHierarchyId": false, + "UseInflector": true, + "UseLegacyPluralizer": false, + "UseManyToManyEntity": false, + "UseNoDefaultConstructor": false, + "UseNoObjectFilter": false, + "UseNodaTime": false, + "UseNullableReferences": false, + "UseSchemaFolders": false, + "UseSchemaNamespaces": false, + "UseSpatial": false, + "UseT4": false +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api/BaseController.cs b/PointOfSale/Pos.Api/Pos.Api/BaseController.cs new file mode 100644 index 0000000..a12b282 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api/BaseController.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Pos.Api.Database; + +namespace Pos.Api +{ + public class BaseController : Controller + { + private readonly IConfiguration _config; + public BaseController(IConfiguration config) + { + _config = config; + } + public override async Task OnActionExecutionAsync(ActionExecutingContext context, + ActionExecutionDelegate next) + { + HttpRequest request = Request; + string apiKey = request.Headers["ApiKey"].ToString(); + string? apiKeyRequest = _config["ApiKey"]; + if (apiKeyRequest == null) + { + throw new NullReferenceException("ApiKey header is missing"); + } + + if (!apiKeyRequest.Equals(apiKey)) + throw new ArgumentException("ApiKey is wrong"); + } + } +} diff --git a/PointOfSale/Pos.Api/Pos.Api/Controllers/PosController.cs b/PointOfSale/Pos.Api/Pos.Api/Controllers/PosController.cs new file mode 100644 index 0000000..b659346 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api/Controllers/PosController.cs @@ -0,0 +1,151 @@ +using System.Diagnostics.Eventing.Reader; +using System.Net; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Primitives; +using Pos.Api.Database.Data; +using Pos.Api.Database.Models; + +namespace Pos.Api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class PosController : Controller + { + private readonly PosApiContext _context; + private readonly IConfiguration _config; + + public PosController(IConfiguration config, PosApiContext context) + { + _context = context; + _config = config; + } + + //[HttpGet("/update")] + //public DateTime LastUpdate() + //{ + + // LastUpdate? lastUpdate = _context.LastUpdates.SingleOrDefault(); + // if (lastUpdate == null) + // { + // return DateTime.Now; + // } + + // DateTime last = lastUpdate.DateTime.Value; + // lastUpdate.DateTime = DateTime.Now; + // _context.SaveChanges(); + + // return last; + //} + + [HttpPost("/employee")] + public void Employee(Employee employee) + { + ValidateApiKey(); + + Employee? dbEmployee = _context.Employees.SingleOrDefault(c => c.Id == employee.Id); + if (dbEmployee == null) + { + dbEmployee = new Employee(); + dbEmployee.Id = employee.Id; + dbEmployee.IsArchived = employee.IsArchived; + _context.Employees.Add(dbEmployee); + } + dbEmployee.Name = employee.Name; + + _context.SaveChanges(); + } + + [HttpPost("/productgroup")] + public void Product(Productgroup productGroup) + { + ValidateApiKey(); + + Productgroup dbProductGroup = _context.Productgroups.SingleOrDefault(c => c.Id == productGroup.Id); + if (dbProductGroup == null) + { + dbProductGroup = new Productgroup(); + dbProductGroup.Id = productGroup.Id; + _context.Productgroups.Add(dbProductGroup); + } + dbProductGroup.IsArchived = productGroup.IsArchived; + dbProductGroup.Name = productGroup.Name; + dbProductGroup.Index = productGroup.Index; + _context.SaveChanges(); + } + + [HttpPost("/sale")] + public void Sale(Sale sale) + { + ValidateApiKey(); + + Sale? dbSale = _context.Sales.SingleOrDefault(c => c.Id == sale.Id); + if (dbSale == null) + { + dbSale = new Sale(); + dbSale.Id = sale.Id; + _context.Sales.Add(dbSale); + } + dbSale.EmployeeId = sale.EmployeeId; + dbSale.Time = sale.Time; + _context.SaveChanges(); + + } + + [HttpPost("/saleline")] + public void SaleLine(SaleLine saleLine) + { + ValidateApiKey(); + + SaleLine? dbSaleLine = _context.SaleLines.SingleOrDefault(c => c.Id == saleLine.Id); + if (dbSaleLine == null) + { + dbSaleLine = new SaleLine(); + dbSaleLine.Id = saleLine.Id; + _context.SaleLines.Add(dbSaleLine); + } + dbSaleLine.SaleId = saleLine.SaleId; + dbSaleLine.Price = saleLine.Price; + dbSaleLine.Pieces = saleLine.Pieces; + dbSaleLine.Price = saleLine.Price; + dbSaleLine.Product = saleLine.Product; + dbSaleLine.Total = saleLine.Total; + _context.SaveChanges(); + } + + [HttpPost("/payment")] + public void Payment(Payment payment) + { + ValidateApiKey(); + + Payment? dbPayment = _context.Payments.SingleOrDefault(c => c.Id == payment.Id); + if (dbPayment == null) + { + dbPayment = new Payment(); + dbPayment.Id = payment.Id; + _context.Payments.Add(dbPayment); + } + dbPayment.Amount = payment.Amount; + dbPayment.SaleId = payment.SaleId; + dbPayment.Type = payment.Type; + _context.SaveChanges(); + } + + private void ValidateApiKey() + { + IHeaderDictionary headers = Request.Headers; + if (!headers.ContainsKey("ApiKey")) + { + throw new MissingFieldException("ApiKey missing"); + } + + StringValues apiKeyHeader = headers["ApiKey"]; + string? apiKey = _config["ApiKey"]; + if (!apiKeyHeader.Equals(apiKey)) + { + throw new MissingFieldException("ApiKey is wrong"); + } + + } + + } +} diff --git a/PointOfSale/Pos.Api/Pos.Api/Controllers/WeatherForecastController.cs b/PointOfSale/Pos.Api/Pos.Api/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..34e22a7 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api/Controllers/WeatherForecastController.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Pos.Api.Controllers +{ + [ApiController] + [Route("[controller]")] + public class WeatherForecastController : ControllerBase + { + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api/Helper/ApiKeyHeaderOperationFilter.cs b/PointOfSale/Pos.Api/Pos.Api/Helper/ApiKeyHeaderOperationFilter.cs new file mode 100644 index 0000000..a806019 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api/Helper/ApiKeyHeaderOperationFilter.cs @@ -0,0 +1,33 @@ +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Pos.Api.Database; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Pos.Api.Helper +{ + public class ApiKeyHeaderOperationFilter : IOperationFilter + { + private readonly IConfiguration _config; + + public ApiKeyHeaderOperationFilter(IConfiguration config) + { + _config = config; + } + + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + string? apiKey = _config["ApiKey"]; + operation.Parameters.Add(new OpenApiParameter + { + Name = "ApiKey", + In = ParameterLocation.Header, + Required = true, + Schema = new OpenApiSchema + { + Type = "string", + Default = new OpenApiString(apiKey) + } + }); + } + } +} diff --git a/PointOfSale/Pos.Api/Pos.Api/Helper/LoadConfig.cs b/PointOfSale/Pos.Api/Pos.Api/Helper/LoadConfig.cs new file mode 100644 index 0000000..7220fb3 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api/Helper/LoadConfig.cs @@ -0,0 +1,55 @@ +using Microsoft.Extensions.Configuration; + +namespace Pos.Api.Helper +{ + public class LoadConfig + { + private string dir; + public LoadConfig() + { + dir = AppDomain.CurrentDomain.BaseDirectory; + } + public IConfiguration ByEnvironment() + { + //Console.Out.WriteLine($"Json path: {dir}"); +#if DEBUG + + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) + .Build(); + return config; + +#else + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true) + .Build(); + return config; +#endif + //Running in a environment that not is supported in this setup + throw new Exception("HostingEnvironment is not supported! This config setup only supports Development or Production"); + } + + public IConfiguration DebugConfiguration() + { + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) + .Build(); + return config; + } + + public IConfiguration ReleaseConfiguration() + { + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true) + .Build(); + return config; + } + } +} diff --git a/PointOfSale/Pos.Api/Pos.Api/Pos.Api.csproj b/PointOfSale/Pos.Api/Pos.Api/Pos.Api.csproj new file mode 100644 index 0000000..b83da3f --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api/Pos.Api.csproj @@ -0,0 +1,24 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + Always + + + + diff --git a/PointOfSale/Pos.Api/Pos.Api/Program.cs b/PointOfSale/Pos.Api/Pos.Api/Program.cs new file mode 100644 index 0000000..509d9ec --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api/Program.cs @@ -0,0 +1,32 @@ +using Pos.Api.Database; +using Pos.Api.Database.Data; +using Pos.Api.Helper; +using LoadConfig = Pos.Api.Helper.LoadConfig; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +LoadConfig loadConfig = new LoadConfig(); +IConfiguration config = loadConfig.ByEnvironment(); +builder.Services.AddSingleton(config); +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(c => + c.OperationFilter() + ); +builder.Services.AddDbContext(); +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/PointOfSale/Pos.Api/Pos.Api/Properties/launchSettings.json b/PointOfSale/Pos.Api/Pos.Api/Properties/launchSettings.json new file mode 100644 index 0000000..f3b452f --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:53151", + "sslPort": 0 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5297", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/PointOfSale/Pos.Api/Pos.Api/WeatherForecast.cs b/PointOfSale/Pos.Api/Pos.Api/WeatherForecast.cs new file mode 100644 index 0000000..1ff63e9 --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api/WeatherForecast.cs @@ -0,0 +1,13 @@ +namespace Pos.Api +{ + public class WeatherForecast + { + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Api/Pos.Api/appsettings.Development.json b/PointOfSale/Pos.Api/Pos.Api/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/PointOfSale/Pos.Api/Pos.Api/appsettings.json b/PointOfSale/Pos.Api/Pos.Api/appsettings.json new file mode 100644 index 0000000..c228e7b --- /dev/null +++ b/PointOfSale/Pos.Api/Pos.Api/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "MySQL": "Data Source=mysql22.unoeuro.com;Initial Catalog=blomstertilalt_dk_db;Persist Security Info=False;User ID=blomstertil_dk;Password=amccpy6l", + "ApiKey": "ad0YfMYm5bdVGjmXkJBOdNggQaWtkB9nzyQv68GAcB7mpf9onGBP9j3DJ46S7go30NwaQgoZBNS7hZDOM79KTyU3K2ysMW2x4mWGHOkETJmWadaMXBTGpoWn0ef9KiUN" +} diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/LoadConfig.cs b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/LoadConfig.cs new file mode 100644 index 0000000..9cf97e2 --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/LoadConfig.cs @@ -0,0 +1,55 @@ +using Microsoft.Extensions.Configuration; + +namespace Pos.Dispatcher.Database +{ + public class LoadConfig + { + private string dir; + public LoadConfig() + { + dir = AppDomain.CurrentDomain.BaseDirectory; + } + public IConfiguration ByEnvironment() + { + //Console.Out.WriteLine($"Json path: {dir}"); +#if DEBUG + + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) + .Build(); + return config; + +#else + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true) + .Build(); + return config; +#endif + //Running in a environment that not is supported in this setup + throw new Exception("HostingEnvironment is not supported! This config setup only supports Development or Production"); + } + + public IConfiguration DebugConfiguration() + { + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) + .Build(); + return config; + } + + public IConfiguration ReleaseConfiguration() + { + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true) + .Build(); + return config; + } + } +} diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Model/PosDispatcherContext.cs b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Model/PosDispatcherContext.cs new file mode 100644 index 0000000..cb00ca7 --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Model/PosDispatcherContext.cs @@ -0,0 +1,75 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Pos.Dispatcher.Database.Models; + +namespace Pos.Dispatcher.Database.Model; + +public partial class PosDispatcherContext : DbContext +{ + public PosDispatcherContext(DbContextOptions options) + : base(options) + { + } + + public virtual DbSet Employees { get; set; } + + public virtual DbSet Payments { get; set; } + + public virtual DbSet Products { get; set; } + + public virtual DbSet Productgroups { get; set; } + + public virtual DbSet Sales { get; set; } + + public virtual DbSet SaleLines { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .UseCollation("armscii8_bin") + .HasCharSet("armscii8"); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + + entity.Property(e => e.Id).ValueGeneratedOnAdd(); + + entity.HasOne(d => d.IdNavigation).WithOne(p => p.Product) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK_Product_ProductGroup"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + }); + + OnModelCreatingPartial(modelBuilder); + } + + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); +} \ No newline at end of file diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Model/PosDispatcherContextExtension.cs b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Model/PosDispatcherContextExtension.cs new file mode 100644 index 0000000..9c597e5 --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Model/PosDispatcherContextExtension.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Pos.Dispatcher.Database.Model +{ + public partial class PosDispatcherContext + { + protected override void OnConfiguring( + DbContextOptionsBuilder optionsBuilder) + { + LoadConfig l = new LoadConfig(); + IConfiguration config = l.ByEnvironment(); + string connectionString = config["MariaSqlServer"].ToString(); + optionsBuilder + .UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)) + .UseLoggerFactory(LoggerFactory.Create(b => b + .AddFilter(level => level >= LogLevel.Information))) + .EnableSensitiveDataLogging() + .EnableDetailedErrors(); + base.OnConfiguring(optionsBuilder); + } + } +} diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Employee.cs b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Employee.cs new file mode 100644 index 0000000..f2cb620 --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Employee.cs @@ -0,0 +1,29 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Pos.Dispatcher.Database.Models; + +[Table("employee")] +[MySqlCharSet("utf8mb4")] +[MySqlCollation("utf8mb4_general_ci")] +public partial class Employee +{ + [Key] + [Column(TypeName = "int(11)")] + public int Id { get; set; } + + [Required] + [StringLength(50)] + public string Name { get; set; } + + [Column(TypeName = "tinyint(4)")] + public sbyte IsArchived { get; set; } + + [Column(TypeName = "tinyint(4)")] + public sbyte? IsModified { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Payment.cs b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Payment.cs new file mode 100644 index 0000000..f9209b9 --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Payment.cs @@ -0,0 +1,27 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Pos.Dispatcher.Database.Models; + +[Table("payment")] +public partial class Payment +{ + [Key] + [Column(TypeName = "int(11)")] + public int Id { get; set; } + + [Column(TypeName = "int(11)")] + public int SaleId { get; set; } + + [Precision(20, 6)] + public decimal Amount { get; set; } + + [Required] + [Column(TypeName = "tinytext")] + public string Type { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Product.cs b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Product.cs new file mode 100644 index 0000000..98ec3be --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Product.cs @@ -0,0 +1,46 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Pos.Dispatcher.Database.Models; + +[Table("product")] +[Index("ProductGroupId", Name = "FK_Product_Categories")] +[MySqlCharSet("utf8mb4")] +[MySqlCollation("utf8mb4_general_ci")] +public partial class Product +{ + [Key] + [Column(TypeName = "int(11)")] + public int Id { get; set; } + + [Required] + [Column(TypeName = "tinytext")] + public string Name { get; set; } + + [Precision(10, 2)] + public decimal Price { get; set; } + + [Column(TypeName = "mediumtext")] + public string Description { get; set; } + + [Column(TypeName = "int(11)")] + public int ProductGroupId { get; set; } + + [Column(TypeName = "tinyint(4)")] + public sbyte IsArchived { get; set; } + + [Column(TypeName = "int(11)")] + public int Index { get; set; } + + [Column(TypeName = "tinyint(4)")] + public sbyte? IsModified { get; set; } + + [ForeignKey("Id")] + [InverseProperty("Product")] + public virtual Productgroup IdNavigation { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Productgroup.cs b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Productgroup.cs new file mode 100644 index 0000000..826a7fc --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Productgroup.cs @@ -0,0 +1,35 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Pos.Dispatcher.Database.Models; + +[Table("productgroup")] +[MySqlCharSet("utf8mb4")] +[MySqlCollation("utf8mb4_general_ci")] +public partial class Productgroup +{ + [Key] + [Column(TypeName = "int(11)")] + public int Id { get; set; } + + [Required] + [Column(TypeName = "tinytext")] + public string Name { get; set; } + + [Column(TypeName = "tinyint(4)")] + public sbyte IsArchived { get; set; } + + [Column(TypeName = "int(11)")] + public int Index { get; set; } + + [Column(TypeName = "tinyint(4)")] + public sbyte? IsModified { get; set; } + + [InverseProperty("IdNavigation")] + public virtual Product Product { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Sale.cs b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Sale.cs new file mode 100644 index 0000000..1ff3626 --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/Sale.cs @@ -0,0 +1,26 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Pos.Dispatcher.Database.Models; + +[Table("sale")] +public partial class Sale +{ + [Key] + [Column(TypeName = "int(11)")] + public int Id { get; set; } + + [Column(TypeName = "datetime")] + public DateTime Time { get; set; } + + [Column(TypeName = "int(11)")] + public int EmployeeId { get; set; } + + [Column(TypeName = "tinyint(4)")] + public sbyte? IsModified { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/SaleLine.cs b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/SaleLine.cs new file mode 100644 index 0000000..8872727 --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Models/SaleLine.cs @@ -0,0 +1,33 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Pos.Dispatcher.Database.Models; + +[Table("sale_line")] +public partial class SaleLine +{ + [Key] + [Column(TypeName = "int(11)")] + public int Id { get; set; } + + [Column(TypeName = "int(11)")] + public int SaleId { get; set; } + + [Required] + [Column(TypeName = "tinytext")] + public string Product { get; set; } + + [Column(TypeName = "smallint(6)")] + public short Pieces { get; set; } + + [Precision(20, 6)] + public decimal Price { get; set; } + + [Precision(20, 6)] + public decimal Total { get; set; } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Pos.Dispatcher.Database.csproj b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Pos.Dispatcher.Database.csproj new file mode 100644 index 0000000..37728aa --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/Pos.Dispatcher.Database.csproj @@ -0,0 +1,16 @@ + + + + net7.0 + enable + enable + + + + + + + + + + diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/efpt.config.json b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/efpt.config.json new file mode 100644 index 0000000..2770df9 --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher.Database/efpt.config.json @@ -0,0 +1,62 @@ +{ + "CodeGenerationMode": 3, + "ContextClassName": "PosDispatcherContext", + "ContextNamespace": null, + "DefaultDacpacSchema": null, + "FilterSchemas": false, + "IncludeConnectionString": false, + "ModelNamespace": null, + "OutputContextPath": "Model", + "OutputPath": "Models", + "PreserveCasingWithRegex": true, + "ProjectRootNamespace": "Pos.Dispatcher.Database", + "Schemas": null, + "SelectedHandlebarsLanguage": 0, + "SelectedToBeGenerated": 0, + "Tables": [ + { + "Name": "employee", + "ObjectType": 0 + }, + { + "Name": "payment", + "ObjectType": 0 + }, + { + "Name": "product", + "ObjectType": 0 + }, + { + "Name": "productgroup", + "ObjectType": 0 + }, + { + "Name": "sale", + "ObjectType": 0 + }, + { + "Name": "sale_line", + "ObjectType": 0 + } + ], + "UiHint": "localhost", + "UncountableWords": null, + "UseBoolPropertiesWithoutDefaultSql": false, + "UseDatabaseNames": false, + "UseDateOnlyTimeOnly": false, + "UseDbContextSplitting": false, + "UseFluentApiOnly": false, + "UseHandleBars": false, + "UseHierarchyId": false, + "UseInflector": true, + "UseLegacyPluralizer": false, + "UseManyToManyEntity": false, + "UseNoDefaultConstructor": false, + "UseNoObjectFilter": false, + "UseNodaTime": false, + "UseNullableReferences": false, + "UseSchemaFolders": false, + "UseSchemaNamespaces": false, + "UseSpatial": false, + "UseT4": false +} \ No newline at end of file diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher/Pos.Dispatcher.csproj b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher/Pos.Dispatcher.csproj new file mode 100644 index 0000000..ac02830 --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher/Pos.Dispatcher.csproj @@ -0,0 +1,31 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + Always + true + PreserveNewest + + + + + + + + + + + + + diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher/Program.cs b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher/Program.cs new file mode 100644 index 0000000..f9e7f5f --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher/Program.cs @@ -0,0 +1,21 @@ +// See https://aka.ms/new-console-template for more information + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Pomelo.EntityFrameworkCore; +using Pos.Dispatcher; +using Pos.Dispatcher.Database; +using Pos.Dispatcher.Database.Model; + +HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); +LoadConfig loadConfig = new LoadConfig(); +IConfiguration config = loadConfig.ByEnvironment(); +builder.Services.AddSingleton(config); +builder.Services.AddDbContext(); +builder.Services.AddScoped(); +using IHost host = builder.Build(); + +Synchronize synchronize = host.Services.GetRequiredService(); +synchronize.Go(); \ No newline at end of file diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher/Synchronize.cs b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher/Synchronize.cs new file mode 100644 index 0000000..3d3b85e --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher/Synchronize.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Pos.Dispatcher.Database.Model; +using Pos.Dispatcher.Database.Models; +using RestSharp; + +namespace Pos.Dispatcher +{ + public class Synchronize + { + private readonly PosDispatcherContext _context; + private readonly IConfiguration _config; + private string _apiUrl; + private string _apiKey; + + public Synchronize(PosDispatcherContext context, IConfiguration config) + { + _context = context; + _config = config; + _apiUrl = _config["PosApi"]; + _apiKey = _config["ApiKey"]; + } + + public void Go() + { + Employee(); + ProductGroup(); + Sale(); + } + + private void Employee() + { + List employees = _context.Employees.Where(c => c.IsModified == 1).ToList(); + foreach (Employee employee in employees) + { + RestClient client = new RestClient(_apiUrl); + client.AddDefaultHeader("ApiKey", _apiKey); + var request = new RestRequest("/employee", Method.Post); + request.AddJsonBody(employee); + RestResponse restResponse = client.Post(request); + employee.IsModified = 0; + } + + _context.SaveChanges(); + } + + private void ProductGroup() + { + List productgroups = _context.Productgroups.Where(c => c.IsModified == 1).ToList(); + foreach (Productgroup product in productgroups) + { + RestClient client = new RestClient(_apiUrl); + client.AddDefaultHeader("ApiKey", _apiKey); + var request = new RestRequest("/productgroup", Method.Post); + request.AddJsonBody(product); + RestResponse response = client.Post(request); + product.IsModified = 0; + } + _context.SaveChanges(); + } + + public void Sale() + { + List sales = _context.Sales.Where(c => c.IsModified == 1).ToList(); + foreach (Sale sale in sales) + { + RestClient client = new RestClient(_apiUrl); + client.AddDefaultHeader("ApiKey", _apiKey); + var request = new RestRequest("/sale", Method.Post); + request.AddJsonBody(sale); + RestResponse response = client.Post(request); + sale.IsModified = 0; + + List saleLines = _context.SaleLines.Where(c => c.SaleId == sale.Id).ToList(); + foreach (SaleLine saleLine in saleLines) + { + client = new RestClient(_apiUrl); + client.AddDefaultHeader("ApiKey", _apiKey); + request = new RestRequest("/saleline", Method.Post); + request.AddJsonBody(saleLine); + response = client.Post(request); + } + + List payments = _context.Payments.Where(c => c.SaleId == sale.Id).ToList(); + foreach (Payment payment in payments) + { + client = new RestClient(_apiUrl); + client.AddDefaultHeader("ApiKey", _apiKey); + request = new RestRequest("/payment", Method.Post); + request.AddJsonBody(payment); + response = client.Post(request); + } + + sale.IsModified = 0; + } + _context.SaveChanges(); + } + } +} diff --git a/PointOfSale/Pos.Dispatcher/Pos.Dispatcher/appsettings.json b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher/appsettings.json new file mode 100644 index 0000000..b92180e --- /dev/null +++ b/PointOfSale/Pos.Dispatcher/Pos.Dispatcher/appsettings.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "MariaSqlServer": "Data Source=localhost;Initial Catalog=PointOfSale;Persist Security Info=False;User ID=root", + "ApiKey": "ad0YfMYm5bdVGjmXkJBOdNggQaWtkB9nzyQv68GAcB7mpf9onGBP9j3DJ46S7go30NwaQgoZBNS7hZDOM79KTyU3K2ysMW2x4mWGHOkETJmWadaMXBTGpoWn0ef9KiUN", + "PosApi": "http://localhost:53151" +} diff --git a/PointOfSale/Pos.EpsonPrinter/EncodingHelper.cs b/PointOfSale/Pos.EpsonPrinter/EncodingHelper.cs new file mode 100644 index 0000000..82fdcaa --- /dev/null +++ b/PointOfSale/Pos.EpsonPrinter/EncodingHelper.cs @@ -0,0 +1,21 @@ +using System.Text; + +namespace EpsonReceiptPrinter; + +public static class EncodingHelper +{ + static EncodingHelper() + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + } + + public static Encoding GetEncodingForCodePage(PrinterCodePage codePage) => + codePage switch + { + PrinterCodePage.PC865_Nordic => Encoding.GetEncoding(865), + PrinterCodePage.PC850_Multilingual => Encoding.GetEncoding(850), + PrinterCodePage.PC858_Euro => Encoding.GetEncoding(858), + PrinterCodePage.WPC1252 => Encoding.GetEncoding(1252), + _ => Encoding.GetEncoding(865) + }; +} diff --git a/PointOfSale/Pos.EpsonPrinter/EpsonNetworkReceiptPrinter.cs b/PointOfSale/Pos.EpsonPrinter/EpsonNetworkReceiptPrinter.cs new file mode 100644 index 0000000..9c16aa8 --- /dev/null +++ b/PointOfSale/Pos.EpsonPrinter/EpsonNetworkReceiptPrinter.cs @@ -0,0 +1,42 @@ +using System.Net.Sockets; + +namespace EpsonReceiptPrinter; + +public sealed class EpsonNetworkReceiptPrinter : IReceiptPrinter +{ + private readonly string _host; + private readonly int _port; + private readonly PrinterCodePage _codePage; + + public EpsonNetworkReceiptPrinter(string host, int port = 9100, PrinterCodePage codePage = PrinterCodePage.PC865_Nordic) + { + if (string.IsNullOrWhiteSpace(host)) + { + throw new ArgumentException("Host must be provided.", nameof(host)); + } + + _host = host; + _port = port; + _codePage = codePage; + } + + public async Task PrintAsync(Receipt receipt, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(receipt); + + var data = ReceiptFormatter.Format(receipt, _codePage); + await PrintRawAsync(data, cancellationToken); + } + + public async Task PrintRawAsync(byte[] data, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(data); + + using var client = new TcpClient(); + await client.ConnectAsync(_host, _port, cancellationToken); + + await using var stream = client.GetStream(); + await stream.WriteAsync(data, cancellationToken); + await stream.FlushAsync(cancellationToken); + } +} diff --git a/PointOfSale/Pos.EpsonPrinter/EpsonReceiptPrinter.csproj b/PointOfSale/Pos.EpsonPrinter/EpsonReceiptPrinter.csproj new file mode 100644 index 0000000..7ee8bff --- /dev/null +++ b/PointOfSale/Pos.EpsonPrinter/EpsonReceiptPrinter.csproj @@ -0,0 +1,10 @@ + + + + net8.0 + enable + enable + false + + + diff --git a/PointOfSale/Pos.EpsonPrinter/EscPosBuilder.cs b/PointOfSale/Pos.EpsonPrinter/EscPosBuilder.cs new file mode 100644 index 0000000..605babe --- /dev/null +++ b/PointOfSale/Pos.EpsonPrinter/EscPosBuilder.cs @@ -0,0 +1,147 @@ +using System.Text; + +namespace EpsonReceiptPrinter; + +public sealed class EscPosBuilder +{ + private readonly List _buffer = new(); + private readonly Encoding _encoding; + private PrinterCodePage _codePage; + + public EscPosBuilder(PrinterCodePage codePage = PrinterCodePage.PC865_Nordic, Encoding? encoding = null) + { + _codePage = codePage; + _encoding = encoding ?? EncodingHelper.GetEncodingForCodePage(codePage); + } + + public EscPosBuilder Initialize() + { + _buffer.AddRange(EscPosCommands.Initialize); + _buffer.AddRange(EscPosCommands.SelectCodePage(_codePage)); + return this; + } + + public EscPosBuilder SelectCodePage(PrinterCodePage codePage) + { + _codePage = codePage; + _buffer.AddRange(EscPosCommands.SelectCodePage(codePage)); + return this; + } + + public EscPosBuilder AlignLeft() + { + _buffer.AddRange(EscPosCommands.AlignLeft); + return this; + } + + public EscPosBuilder AlignCenter() + { + _buffer.AddRange(EscPosCommands.AlignCenter); + return this; + } + + public EscPosBuilder AlignRight() + { + _buffer.AddRange(EscPosCommands.AlignRight); + return this; + } + + public EscPosBuilder BoldOn() + { + _buffer.AddRange(EscPosCommands.BoldOn); + return this; + } + + public EscPosBuilder BoldOff() + { + _buffer.AddRange(EscPosCommands.BoldOff); + return this; + } + + public EscPosBuilder UnderlineOn() + { + _buffer.AddRange(EscPosCommands.UnderlineOn); + return this; + } + + public EscPosBuilder UnderlineOff() + { + _buffer.AddRange(EscPosCommands.UnderlineOff); + return this; + } + + public EscPosBuilder SetTextSize(TextSize size) + { + _buffer.AddRange(EscPosCommands.TextSize(size)); + return this; + } + + public EscPosBuilder SetTextSize(int widthMultiplier, int heightMultiplier) + { + _buffer.AddRange(EscPosCommands.TextSize(widthMultiplier, heightMultiplier)); + return this; + } + + public EscPosBuilder Write(string text) + { + if (!string.IsNullOrEmpty(text)) + { + _buffer.AddRange(_encoding.GetBytes(text)); + } + + return this; + } + + public EscPosBuilder WriteLine(string? text = null) + { + if (!string.IsNullOrEmpty(text)) + { + _buffer.AddRange(_encoding.GetBytes(text)); + } + + _buffer.Add(0x0A); + return this; + } + + public EscPosBuilder Feed(int lines = 1) + { + _buffer.AddRange(EscPosCommands.Feed(lines)); + return this; + } + + public EscPosBuilder Cut(bool partial = false) + { + _buffer.AddRange(partial ? EscPosCommands.CutPartial() : EscPosCommands.CutFull()); + return this; + } + + public EscPosBuilder OpenCashDrawer() + { + _buffer.AddRange(EscPosCommands.OpenCashDrawer()); + return this; + } + + public EscPosBuilder Separator(int width = 42, char character = '-') + { + return WriteLine(new string(character, width)); + } + + public EscPosBuilder WriteColumns(string left, string? right, int totalWidth = 42) + { + left ??= string.Empty; + right ??= string.Empty; + + if (left.Length + right.Length >= totalWidth) + { + WriteLine(left); + WriteLine(right.PadLeft(totalWidth)); + return this; + } + + var spaces = totalWidth - left.Length - right.Length; + WriteLine(left + new string(' ', spaces) + right); + return this; + } + + public byte[] Build() => _buffer.ToArray(); +} diff --git a/PointOfSale/Pos.EpsonPrinter/EscPosCommands.cs b/PointOfSale/Pos.EpsonPrinter/EscPosCommands.cs new file mode 100644 index 0000000..f59040c --- /dev/null +++ b/PointOfSale/Pos.EpsonPrinter/EscPosCommands.cs @@ -0,0 +1,63 @@ +namespace EpsonReceiptPrinter; + +public static class EscPosCommands +{ + public static readonly byte[] Initialize = [0x1B, 0x40]; + + public static readonly byte[] BoldOn = [0x1B, 0x45, 0x01]; + public static readonly byte[] BoldOff = [0x1B, 0x45, 0x00]; + + public static readonly byte[] UnderlineOn = [0x1B, 0x2D, 0x01]; + public static readonly byte[] UnderlineOff = [0x1B, 0x2D, 0x00]; + + public static readonly byte[] AlignLeft = [0x1B, 0x61, 0x00]; + public static readonly byte[] AlignCenter = [0x1B, 0x61, 0x01]; + public static readonly byte[] AlignRight = [0x1B, 0x61, 0x02]; + + public static readonly byte[] FontA = [0x1B, 0x4D, 0x00]; + public static readonly byte[] FontB = [0x1B, 0x4D, 0x01]; + + public static byte[] Feed(int lines) + { + if (lines < 0 || lines > 255) + { + throw new ArgumentOutOfRangeException(nameof(lines)); + } + + return [0x1B, 0x64, (byte)lines]; + } + + public static byte[] CutFull() => [0x1D, 0x56, 0x00]; + + public static byte[] CutPartial() => [0x1D, 0x56, 0x01]; + + public static byte[] OpenCashDrawer() => [0x1B, 0x70, 0x00, 0x19, 0xFA]; + + public static byte[] SelectCodePage(PrinterCodePage codePage) => [0x1B, 0x74, (byte)codePage]; + + public static byte[] TextSize(TextSize size) => + size switch + { + TextSize.Normal => [0x1D, 0x21, 0x00], + TextSize.DoubleWidth => [0x1D, 0x21, 0x10], + TextSize.DoubleHeight => [0x1D, 0x21, 0x01], + TextSize.DoubleWidthAndHeight => [0x1D, 0x21, 0x11], + _ => [0x1D, 0x21, 0x00] + }; + + public static byte[] TextSize(int widthMultiplier, int heightMultiplier) + { + if (widthMultiplier < 1 || widthMultiplier > 8) + { + throw new ArgumentOutOfRangeException(nameof(widthMultiplier)); + } + + if (heightMultiplier < 1 || heightMultiplier > 8) + { + throw new ArgumentOutOfRangeException(nameof(heightMultiplier)); + } + + var value = (byte)(((widthMultiplier - 1) << 4) | (heightMultiplier - 1)); + return [0x1D, 0x21, value]; + } +} diff --git a/PointOfSale/Pos.EpsonPrinter/IReceiptPrinter.cs b/PointOfSale/Pos.EpsonPrinter/IReceiptPrinter.cs new file mode 100644 index 0000000..45b50d6 --- /dev/null +++ b/PointOfSale/Pos.EpsonPrinter/IReceiptPrinter.cs @@ -0,0 +1,7 @@ +namespace EpsonReceiptPrinter; + +public interface IReceiptPrinter +{ + Task PrintAsync(Receipt receipt, CancellationToken cancellationToken = default); + Task PrintRawAsync(byte[] data, CancellationToken cancellationToken = default); +} diff --git a/PointOfSale/Pos.EpsonPrinter/PrinterCodePage.cs b/PointOfSale/Pos.EpsonPrinter/PrinterCodePage.cs new file mode 100644 index 0000000..c00739e --- /dev/null +++ b/PointOfSale/Pos.EpsonPrinter/PrinterCodePage.cs @@ -0,0 +1,13 @@ +namespace EpsonReceiptPrinter; + +public enum PrinterCodePage +{ + PC437_USA = 0, + Katakana = 1, + PC850_Multilingual = 2, + PC860_Portuguese = 3, + PC863_CanadianFrench = 4, + PC865_Nordic = 5, + WPC1252 = 16, + PC858_Euro = 19 +} diff --git a/PointOfSale/Pos.EpsonPrinter/Receipt.cs b/PointOfSale/Pos.EpsonPrinter/Receipt.cs new file mode 100644 index 0000000..b6a11aa --- /dev/null +++ b/PointOfSale/Pos.EpsonPrinter/Receipt.cs @@ -0,0 +1,31 @@ +namespace EpsonReceiptPrinter; + +public sealed class Receipt +{ + public string? Header { get; set; } + public string? SubHeader { get; set; } + public List Lines { get; } = new(); + public string? Footer { get; set; } + public bool CutPaper { get; set; } = true; + public int PaperWidthCharacters { get; set; } = 42; +} + +public sealed class ReceiptLine +{ + public ReceiptLine() + { + } + + public ReceiptLine(string left, string? right = null, bool emphasize = false, TextSize textSize = TextSize.Normal) + { + Left = left; + Right = right; + Emphasize = emphasize; + TextSize = textSize; + } + + public string Left { get; set; } = string.Empty; + public string? Right { get; set; } + public bool Emphasize { get; set; } + public TextSize TextSize { get; set; } = TextSize.Normal; +} diff --git a/PointOfSale/Pos.EpsonPrinter/ReceiptFormatter.cs b/PointOfSale/Pos.EpsonPrinter/ReceiptFormatter.cs new file mode 100644 index 0000000..1e49820 --- /dev/null +++ b/PointOfSale/Pos.EpsonPrinter/ReceiptFormatter.cs @@ -0,0 +1,72 @@ +namespace EpsonReceiptPrinter; + +public static class ReceiptFormatter +{ + public static byte[] Format(Receipt receipt, PrinterCodePage codePage = PrinterCodePage.PC865_Nordic) + { + ArgumentNullException.ThrowIfNull(receipt); + + var width = receipt.PaperWidthCharacters <= 0 ? 42 : receipt.PaperWidthCharacters; + + var builder = new EscPosBuilder(codePage) + .Initialize() + .AlignCenter(); + + if (!string.IsNullOrWhiteSpace(receipt.Header)) + { + builder + .SetTextSize(TextSize.DoubleWidthAndHeight) + .BoldOn() + .WriteLine(receipt.Header) + .BoldOff() + .SetTextSize(TextSize.Normal); + } + + if (!string.IsNullOrWhiteSpace(receipt.SubHeader)) + { + builder.WriteLine(receipt.SubHeader); + } + + builder + .Feed(1) + .AlignLeft() + .Separator(width); + + foreach (var line in receipt.Lines) + { + builder.SetTextSize(line.TextSize); + + if (line.Emphasize) + { + builder.BoldOn(); + } + else + { + builder.BoldOff(); + } + + builder.WriteColumns(line.Left, line.Right, width); + builder.SetTextSize(TextSize.Normal); + builder.BoldOff(); + } + + builder + .Separator(width) + .Feed(1) + .AlignCenter(); + + if (!string.IsNullOrWhiteSpace(receipt.Footer)) + { + builder.WriteLine(receipt.Footer); + } + + builder.Feed(3); + + if (receipt.CutPaper) + { + builder.Cut(); + } + + return builder.Build(); + } +} diff --git a/PointOfSale/Pos.EpsonPrinter/TextSize.cs b/PointOfSale/Pos.EpsonPrinter/TextSize.cs new file mode 100644 index 0000000..348a483 --- /dev/null +++ b/PointOfSale/Pos.EpsonPrinter/TextSize.cs @@ -0,0 +1,9 @@ +namespace EpsonReceiptPrinter; + +public enum TextSize +{ + Normal, + DoubleWidth, + DoubleHeight, + DoubleWidthAndHeight +} diff --git a/PointOfSale/Pos.Ui/Database/.config/dotnet-tools.json b/PointOfSale/Pos.Ui/Database/.config/dotnet-tools.json new file mode 100644 index 0000000..06b8682 --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "5.0.9", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Ui/Database/Database.csproj b/PointOfSale/Pos.Ui/Database/Database.csproj new file mode 100644 index 0000000..4fc5e95 --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/Database.csproj @@ -0,0 +1,16 @@ + + + + net6.0 + + + + + + + + + + + + diff --git a/PointOfSale/Pos.Ui/Database/LoadConfig.cs b/PointOfSale/Pos.Ui/Database/LoadConfig.cs new file mode 100644 index 0000000..729eb19 --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/LoadConfig.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; + +namespace Pos +{ + public class LoadConfig + { + private string dir; + public LoadConfig() + { + dir = AppDomain.CurrentDomain.BaseDirectory; + } + public IConfiguration ByEnvironment() + { + //Console.Out.WriteLine($"Json path: {dir}"); +#if DEBUG + + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) + .Build(); + return config; + +#else + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true) + .Build(); + return config; +#endif + //Running in a environment that not is supported in this setup + throw new Exception("HostingEnvironment is not supported! This config setup only supports Development or Production"); + } + + public IConfiguration DebugConfiguration() + { + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) + .Build(); + return config; + } + + public IConfiguration ReleaseConfiguration() + { + var config = new ConfigurationBuilder() + .SetBasePath(dir) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true) + .Build(); + return config; + } + } +} diff --git a/PointOfSale/Pos.Ui/Database/Models/EmployeeEntity.cs b/PointOfSale/Pos.Ui/Database/Models/EmployeeEntity.cs new file mode 100644 index 0000000..b71be5e --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/Models/EmployeeEntity.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Database.Models +{ + [Table("employee")] + public class EmployeeEntity + { + [Key] + public int Id { get; set; } + public string Name { get; set; } + public bool IsArchived { get; set; } + public bool IsModified { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/Database/Models/PaymentEntity.cs b/PointOfSale/Pos.Ui/Database/Models/PaymentEntity.cs new file mode 100644 index 0000000..96110c5 --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/Models/PaymentEntity.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Database.Models +{ + [Table("payment")] + public class PaymentEntity + { + [Key] + public int Id { get; set; } + public int SaleId { get; set; } + public decimal Amount { get; set;} + public string Type { get; set; } + + } +} diff --git a/PointOfSale/Pos.Ui/Database/Models/ProductEntity.cs b/PointOfSale/Pos.Ui/Database/Models/ProductEntity.cs new file mode 100644 index 0000000..ef21e4d --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/Models/ProductEntity.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Database.Models +{ + [Table("product")] + public class ProductEntity + { + [Key] + public int Id { get; set; } + public string Name { get; set; } + public decimal Price { get; set; } + public string Description { get; set; } + public bool IsArchived { get; set; } + public int Index { get; set; } + public bool IsModified { get; set; } + public int ProductGroupId { get; set; } + + [ForeignKey(nameof(ProductGroupId))] + public ProductGroupEntity ProductGroup { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/Database/Models/ProductGroupEntity.cs b/PointOfSale/Pos.Ui/Database/Models/ProductGroupEntity.cs new file mode 100644 index 0000000..8f1a6aa --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/Models/ProductGroupEntity.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Database.Models +{ + [Table("productgroup")] + public class ProductGroupEntity + { + [Key] + public int Id { get; set; } + public string Name { get; set; } + public bool IsArchived { get; set; } + public int Index { get; set; } + + public ICollection Products { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/Database/Models/SaleEntity.cs b/PointOfSale/Pos.Ui/Database/Models/SaleEntity.cs new file mode 100644 index 0000000..c4a6b3f --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/Models/SaleEntity.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Database.Models +{ + [Table("sale")] + public class SaleEntity + { + [Key] + public int Id { get; set; } + public DateTime Time { get; set; } + public int EmployeeId { get; set; } + public bool IsModified { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/Database/Models/SaleLineEntity.cs b/PointOfSale/Pos.Ui/Database/Models/SaleLineEntity.cs new file mode 100644 index 0000000..0b90055 --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/Models/SaleLineEntity.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Database.Models +{ + [Table("sale_line")] + public class SaleLineEntity + { + [Key] + public int Id { get; set; } + public int SaleId { get; set; } + public string Product { get; set; } + public int Pieces { get; set; } + public decimal Price { get; set; } + public decimal Total { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/Database/PosDbContext.cs b/PointOfSale/Pos.Ui/Database/PosDbContext.cs new file mode 100644 index 0000000..f60fa07 --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/PosDbContext.cs @@ -0,0 +1,35 @@ +using Database.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Pos; + + +namespace Database +{ + public class PosDbContext : DbContext + { + + public DbSet Employee { get; set; } + public DbSet ProductGroups { get; set; } + public DbSet Products { get; set; } + public DbSet Sales { get; set; } + public DbSet SalesLines { get; set; } + public DbSet Payment { get; set; } + + protected override void OnConfiguring( + DbContextOptionsBuilder optionsBuilder) + { + LoadConfig l = new LoadConfig(); + IConfiguration config = l.ByEnvironment(); + string connectionString = config["MariaSqlServer"].ToString(); + optionsBuilder + .UseMySql(connectionString,ServerVersion.AutoDetect(connectionString)) + .UseLoggerFactory(LoggerFactory.Create(b => b + .AddFilter(level => level >= LogLevel.Information))) + .EnableSensitiveDataLogging() + .EnableDetailedErrors(); + base.OnConfiguring(optionsBuilder); + } + } +} diff --git a/PointOfSale/Pos.Ui/Database/Repository/EmployeeRepository.cs b/PointOfSale/Pos.Ui/Database/Repository/EmployeeRepository.cs new file mode 100644 index 0000000..d5f24c6 --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/Repository/EmployeeRepository.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Database.Models; +using Microsoft.Extensions.Configuration; + +namespace Database.Repository +{ + public class EmployeeRepository : IDisposable + { + + public void Add(string name) + { + EmployeeEntity employee = new EmployeeEntity(); + employee.Name = name; + employee.IsModified = true; + using PosDbContext context = new PosDbContext(); + context.Employee.Add(employee); + context.SaveChanges(); + } + + public EmployeeEntity Get(int employeeId) + { + using PosDbContext context = new PosDbContext(); + EmployeeEntity employeeEntity = context.Employee.First(c => c.Id == employeeId); + return employeeEntity; + } + + public List GetAll() + { + using PosDbContext context = new PosDbContext(); + List staffs = context.Employee.Where(c => c.IsArchived == false).ToList(); + + return staffs; + } + + public void Edit(int id, string name) + { + using PosDbContext context = new PosDbContext(); + EmployeeEntity employee = context.Employee.Single(c => c.Id == id); + employee.Name = name; + employee.IsModified = true; + context.SaveChanges(); + } + + public void Delete(int id) + { + using PosDbContext context = new PosDbContext(); + EmployeeEntity employee = context.Employee.Single(c => c.Id == id); + employee.IsArchived = true; + employee.IsModified = true; + context.SaveChanges(); + + } + + public void Dispose() + { + } + } +} diff --git a/PointOfSale/Pos.Ui/Database/Repository/ProductGroupRepository.cs b/PointOfSale/Pos.Ui/Database/Repository/ProductGroupRepository.cs new file mode 100644 index 0000000..ba8c303 --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/Repository/ProductGroupRepository.cs @@ -0,0 +1,79 @@ + using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Database.Models; +using Microsoft.EntityFrameworkCore; + +namespace Database.Repository +{ + public class ProductGroupRepository + { + public List GetAll() + { + using PosDbContext context = new PosDbContext(); + List productGroups = context.ProductGroups + .Include(c => c.Products) + .Where(c => c.IsArchived == false) + .OrderBy(c => c.Index) + .ToList(); + return productGroups; + } + + public ProductGroupEntity Get(int id) + { + using PosDbContext context = new PosDbContext(); + ProductGroupEntity product = context.ProductGroups.Single(c => c.Id == id); + return product; + } + + public void Add(string name) + { + ProductGroupEntity productGroup = new ProductGroupEntity(); + productGroup.Name = name; + using PosDbContext context = new PosDbContext(); + //Get the highest index + ProductGroupEntity highest = context.ProductGroups.OrderByDescending(c => c.Index).Take(1).FirstOrDefault(); + if(highest == null) + { + highest = new ProductGroupEntity(); + highest.Index = 0; + } + productGroup.Index = highest.Index + 1; + context.ProductGroups.Add(productGroup); + context.SaveChanges(); + } + + public void Edit(string name, int id) + { + using PosDbContext context = new PosDbContext(); + ProductGroupEntity productGroup = context.ProductGroups.Single(c => c.Id == id); + productGroup.Name = name; + context.SaveChanges(); + } + + public void Archive(int id) + { + using PosDbContext context = new PosDbContext(); + ProductGroupEntity productGroup = context.ProductGroups.Single(c => c.Id == id); + productGroup.IsArchived = true; + context.SaveChanges(); + } + + public void SetIndex(int id, int index) + { + using PosDbContext context = new PosDbContext(); + ProductGroupEntity productGroup = context.ProductGroups.Single(c => c.Id == id); + productGroup.Index = index; + context.SaveChanges(); + } + + public bool Any() + { + using PosDbContext context = new PosDbContext(); + bool any = context.ProductGroups.Any(); + return any; + } + } +} diff --git a/PointOfSale/Pos.Ui/Database/Repository/ProductRepository.cs b/PointOfSale/Pos.Ui/Database/Repository/ProductRepository.cs new file mode 100644 index 0000000..95c2fd4 --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/Repository/ProductRepository.cs @@ -0,0 +1,87 @@ + + +using System.Collections.Generic; +using System.Linq; +using Database.Models; + +namespace Database.Repository +{ + public class ProductRepository + { + public List GetAll() + { + using PosDbContext context = new PosDbContext(); + List products = context.Products + .Where(c => c.IsArchived == false) + .OrderBy(c => c.Index) + .ToList(); + return products; + } + + public List GetByProductGroup(int id) + { + using PosDbContext context = new PosDbContext(); + List products = context.Products + .Where(c => c.ProductGroupId == id) + .Where(c => c.IsArchived == false) + .OrderBy(c => c.Index) + .ToList(); + return products; + } + + public void SetIndex(int id, int index) + { + using PosDbContext context = new PosDbContext(); + ProductEntity product = context.Products.Single(c => c.Id == id); + product.Index = index; + product.IsModified = true; + context.SaveChanges(); + } + + public ProductEntity GetById(int id) + { + using PosDbContext context = new PosDbContext(); + ProductEntity product = context.Products.Single(c => c.Id == id); + return product; + } + + public void Update(int id, int productGroupId, string name) + { + using PosDbContext context = new PosDbContext(); + ProductEntity dbProduct = context.Products.Single(c => c.Id == id); + dbProduct.ProductGroupId = productGroupId; + dbProduct.Name = name; + dbProduct.IsModified = true; + context.SaveChanges(); + } + + public void Archive(int id) + { + using PosDbContext context = new PosDbContext(); + ProductEntity dbProduct = context.Products.Single(c => c.Id == id); + dbProduct.IsArchived = true; + dbProduct.IsModified = true; + context.SaveChanges(); + } + + public void Add(string name, int productGroupId) + { + + ProductEntity product = new ProductEntity(); + product.Name = name; + product.ProductGroupId = productGroupId; + using PosDbContext context = new PosDbContext(); + //Get the highest index + ProductEntity highest = context.Products.OrderByDescending(c => c.Index).Take(1).FirstOrDefault(); + if (highest == null) + { + highest = new ProductEntity(); + highest.Index = 0; + } + product.Index = highest.Index + 1; + product.IsModified = true; + context.Products.Add(product); + context.SaveChanges(); + } + } +} diff --git a/PointOfSale/Pos.Ui/Database/Repository/SaleRepository.cs b/PointOfSale/Pos.Ui/Database/Repository/SaleRepository.cs new file mode 100644 index 0000000..7fd8da7 --- /dev/null +++ b/PointOfSale/Pos.Ui/Database/Repository/SaleRepository.cs @@ -0,0 +1,83 @@ +using Database.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Database.Repository +{ + public class SaleRepository + { + public SaleEntity New(int employeeId) + { + using PosDbContext context = new PosDbContext(); + SaleEntity saleEntity = new SaleEntity(); + saleEntity.Time = DateTime.Now; + saleEntity.EmployeeId = employeeId; + saleEntity.IsModified = true; + context.Sales.Add(saleEntity); + context.SaveChanges(); + return saleEntity; + } + + public SaleLineEntity SaveSaleLine(SaleLineEntity entity) + { + using PosDbContext context = new PosDbContext(); + context.SalesLines.Add(entity); + context.SaveChanges(); + return entity; + } + + public List GetSaleLineBySaleId(int saleId) + { + using PosDbContext context = new PosDbContext(); + List saleLineEntities = context.SalesLines.Where(c => c.SaleId == saleId).ToList(); + return saleLineEntities; + } + + public PaymentEntity SavePayment(PaymentEntity entity) + { + using PosDbContext context = new PosDbContext(); + context.Payment.Add(entity); + context.SaveChanges(); + return entity; + } + + public List GetPaymentBySaleId(int saleId) + { + using PosDbContext context = new PosDbContext(); + List paymentEntities = context.Payment.Where(c => c.SaleId == saleId).ToList(); + return paymentEntities; + } + + public SaleEntity GetLatest() + { + using PosDbContext context = new PosDbContext(); + SaleEntity saleEntity = context.Sales.OrderByDescending(c => c.Id).Take(1).First(); + return saleEntity; + } + + public SaleEntity Get(int id) + { + using PosDbContext context = new PosDbContext(); + SaleEntity saleEntity = context.Sales.Single(c => c.Id == id); + return saleEntity; + } + + public List GetByDateRange(DateTime start, DateTime end) + { + using PosDbContext context = new PosDbContext(); + List saleEntities = context.Sales.Where(c => c.Time.Date >= start.Date && c.Time.Date <= end.Date).OrderByDescending(c => c.Time).ToList(); + return saleEntities; + } + + public List GetByDateRange(DateTime start) + { + using PosDbContext context = new PosDbContext(); + List saleEntities = context.Sales.Where(c => c.Time.Date == start.Date.Date).OrderByDescending(c => c.Time).ToList(); + return saleEntities; + } + + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Controllers/PosPrinterController.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Controllers/PosPrinterController.cs new file mode 100644 index 0000000..b1606e8 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Controllers/PosPrinterController.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Model; +using EpsonPrinter.Services; + +namespace EpsonPrinter.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class PosPrinterController : ControllerBase + { + /// + /// PrintReceipt receipt, so far only for all Epson Thermal Printers + /// + /// PrintStyle is: + /// None + /// FontB + /// Bold + /// DoubleHeight + /// DoubleWidth + /// Underline + /// There is support for any combination of PrintStyles + /// ---------------------------------------- + /// + /// + /// + [HttpPost] + [Route("Receipt")] + public void PrintReceipt([FromServices] EpsonPrintService epsonPrint, ReceiptModel receiptModel) + { + epsonPrint.PrintReceipt(receiptModel); + } + + [HttpPost] + [Route("SaleOfDay")] + public void PrintSaleOfDay([FromServices] EpsonPrintService epsonPrint, SaleOfDayModel saleOfDayModel) + { + epsonPrint.PrintSaleOfDay(saleOfDayModel); + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Controllers/ValuesController.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Controllers/ValuesController.cs new file mode 100644 index 0000000..557efdc --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Controllers/ValuesController.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace EpsonPrinter.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class ValuesController : ControllerBase + { + [HttpGet] + [Route("HelloWorld")] + public string Get() + { + return "HelloWorld"; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master.zip b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master.zip new file mode 100644 index 0000000..fff0055 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master.zip differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/.gitattributes b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/.github/FUNDING.yml b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/.github/FUNDING.yml new file mode 100644 index 0000000..ecc1a31 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: ['lukevp'] +#patreon: # Replace with a single Patreon username +#open_collective: # Replace with a single Open Collective username +#ko_fi: # Replace with a single Ko-fi username +#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +#liberapay: # Replace with a single Liberapay username +#issuehunt: # Replace with a single IssueHunt username +#otechie: # Replace with a single Otechie username +#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/.gitignore b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/.gitignore new file mode 100644 index 0000000..ff21097 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/.gitignore @@ -0,0 +1,7 @@ +.vs/ +.idea/ +**/obj/ +**/bin/ +push.ps1 +/push.txt +/ESCPOS_NET/ESCPOS_NET.csproj.user diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/ESCPOS_NET.ConsoleTest.csproj b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/ESCPOS_NET.ConsoleTest.csproj new file mode 100644 index 0000000..2996034 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/ESCPOS_NET.ConsoleTest.csproj @@ -0,0 +1,74 @@ + + + + Exe + net5.0 + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + Always + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/Program.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/Program.cs new file mode 100644 index 0000000..a1ebe8d --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/Program.cs @@ -0,0 +1,267 @@ +using ESCPOS_NET.Emitters; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Linq; +using Microsoft.Extensions.Logging; +using System.Threading; + +namespace ESCPOS_NET.ConsoleTest +{ + internal class Program + { + private static BasePrinter printer; + private static ICommandEmitter e; + + static void Main(string[] args) + { + + Console.WriteLine("Welcome to the ESCPOS_NET Test Application!"); + Console.Write("Would you like to see all debug messages? (y/n): "); + var response = Console.ReadLine().Trim().ToLowerInvariant(); + var logLevel = LogLevel.Information; + if (response.Length >= 1 && response[0] == 'y') + { + Console.WriteLine("Debugging enabled!"); + logLevel = LogLevel.Trace; + } + var factory = LoggerFactory.Create(b => b.AddConsole().SetMinimumLevel(logLevel)); + var logger = factory.CreateLogger(); + ESCPOS_NET.Logging.Logger = logger; + + Console.WriteLine("1 ) Test Serial Port"); + Console.WriteLine("2 ) Test Network Printer"); + Console.Write("Choice: "); + string comPort = ""; + string baudRate; + string ip; + string networkPort; + response = Console.ReadLine(); + var valid = new List { "1", "2" }; + if (!valid.Contains(response)) + { + response = "1"; + } + + int choice = int.Parse(response); + + if (choice == 1) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + while (!comPort.StartsWith("COM")) + { + Console.Write("COM Port (enter for default COM5): "); + comPort = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(comPort)) + { + comPort = "COM5"; + } + } + Console.Write("Baud Rate (enter for default 115200): "); + baudRate = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(baudRate)) + { + baudRate = "115200"; + } + printer = new SerialPrinter(portName: comPort, baudRate: 115200); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + Console.Write("File / virtual com path (eg. /dev/usb/lp0): "); + comPort = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(comPort)) + { + comPort = "/dev/usb/lp0"; + } + printer = new FilePrinter(filePath: comPort, false); + } + } + else if (choice == 2) + { + Console.Write("IP Address (eg. 192.168.1.240): "); + ip = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(ip)) + { + ip = "192.168.254.202"; + } + Console.Write("TCP Port (enter for default 9100): "); + networkPort = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(networkPort)) + { + networkPort = "9100"; + } + printer = new NetworkPrinter(settings: new NetworkPrinterSettings() { ConnectionString = $"{ip}:{networkPort}" }); + } + + bool monitor = false; + Thread.Sleep(500); + Console.Write("Turn on Live Status Back Monitoring? (y/n): "); + response = Console.ReadLine().Trim().ToLowerInvariant(); + if (response.Length >= 1 && response[0] == 'y') + { + monitor = true; + } + + e = new EPSON(); + var testCases = new Dictionary() + { + { Option.SingleLinePrinting, "Single Line Printing" }, + { Option.MultiLinePrinting, "Multi-line Printing" }, + { Option.LineSpacing, "Line Spacing" }, + { Option.BarcodeStyles, "Barcode Styles" }, + { Option.BarcodeTypes, "Barcode Types" }, + { Option.TwoDimensionCodes, "2D Codes" }, + { Option.TextStyles, "Text Styles" }, + { Option.FullReceipt, "Full Receipt" }, + { Option.CodePages, "Code Pages (Euro, Katakana, Etc)" }, + { Option.Images, "Images" }, + { Option.LegacyImages, "Legacy Images" }, + { Option.LargeByteArrays, "Large Byte Arrays" }, + { Option.CashDrawerPin2, "Cash Drawer Pin2" }, + { Option.CashDrawerPin5, "Cash Drawer Pin5" }, + { Option.Exit, "Exit" } + + }; + while (true) + { + foreach (var item in testCases) + { + Console.WriteLine($"{(int)item.Key} : {item.Value}"); + } + Console.Write("Execute Test: "); + + if (!int.TryParse(Console.ReadLine(), out choice) || !Enum.IsDefined(typeof(Option), choice)) + { + Console.WriteLine("Invalid entry. Please try again."); + continue; + } + + var enumChoice = (Option)choice; + if (enumChoice == Option.Exit) + { + return; + } + + Console.Clear(); + + if (monitor) + { + printer.Write(e.Initialize()); + printer.Write(e.Enable()); + printer.Write(e.EnableAutomaticStatusBack()); + } + Setup(monitor); + + printer?.Write(e.PrintLine($"== [ Start {testCases[enumChoice]} ] ==")); + + switch (enumChoice) + { + case Option.SingleLinePrinting: + printer.Write(Tests.SingleLinePrinting(e)); + break; + case Option.MultiLinePrinting: + printer.Write(Tests.MultiLinePrinting(e)); + break; + case Option.LineSpacing: + printer.Write(Tests.LineSpacing(e)); + break; + case Option.BarcodeStyles: + printer.Write(Tests.BarcodeStyles(e)); + break; + case Option.BarcodeTypes: + printer.Write(Tests.BarcodeTypes(e)); + break; + case Option.TwoDimensionCodes: + printer.Write(Tests.TwoDimensionCodes(e)); + break; + case Option.TextStyles: + printer.Write(Tests.TextStyles(e)); + break; + case Option.FullReceipt: + printer.Write(Tests.Receipt(e)); + break; + case Option.Images: + printer.Write(Tests.Images(e, false)); + break; + case Option.LegacyImages: + printer.Write(Tests.Images(e, true)); + break; + case Option.LargeByteArrays: + try + { + printer.Write(Tests.TestLargeByteArrays(e)); + } + catch (Exception e) + { + Console.WriteLine($"Aborting print due to test failure. Exception: {e?.Message}, Stack Trace: {e?.GetBaseException()?.StackTrace}"); + } + break; + case Option.CashDrawerPin2: + printer.Write(Tests.CashDrawerOpenPin2(e)); + break; + case Option.CashDrawerPin5: + printer.Write(Tests.CashDrawerOpenPin5(e)); + break; + default: + Console.WriteLine("Invalid entry."); + break; + } + + Setup(monitor); + printer?.Write(e.PrintLine($"== [ End {testCases[enumChoice]} ] ==")); + printer?.Write(e.PartialCutAfterFeed(5)); + + // TODO: also make an automatic runner that runs all tests (command line). + } + } + + public enum Option + { + SingleLinePrinting = 1, + MultiLinePrinting, + LineSpacing, + BarcodeStyles, + BarcodeTypes, + TwoDimensionCodes, + TextStyles, + FullReceipt, + CodePages, + Images, + LegacyImages, + LargeByteArrays, + CashDrawerPin2, + CashDrawerPin5, + Exit = 99 + } + + private static void StatusChanged(object sender, EventArgs ps) + { + var status = (PrinterStatusEventArgs)ps; + if (status == null) { Console.WriteLine("Status was null - unable to read status from printer."); return; } + Console.WriteLine($"Printer Online Status: {status.IsPrinterOnline}"); + Console.WriteLine(JsonConvert.SerializeObject(status)); + } + private static bool _hasEnabledStatusMonitoring = false; + + private static void Setup(bool enableStatusBackMonitoring) + { + if (printer != null) + { + // Only register status monitoring once. + if (!_hasEnabledStatusMonitoring) + { + printer.StatusChanged += StatusChanged; + _hasEnabledStatusMonitoring = true; + } + printer?.Write(e.Initialize()); + printer?.Write(e.Enable()); + if (enableStatusBackMonitoring) + { + printer.Write(e.EnableAutomaticStatusBack()); + } + } + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/Test2DCodes.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/Test2DCodes.cs new file mode 100644 index 0000000..1ecf21b --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/Test2DCodes.cs @@ -0,0 +1,43 @@ +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; + +namespace ESCPOS_NET.ConsoleTest +{ + public static partial class Tests + { + private const string websiteString = "https://github.com/lukevp/ESC-POS-.NET/"; + public static byte[][] TwoDimensionCodes(ICommandEmitter e) => new byte[][] { + e.PrintLine("PDF417:"), + e.Print2DCode(TwoDimensionCodeType.PDF417, websiteString), + e.PrintLine(), + + e.PrintLine("PDF417 (TINY):"), + e.Print2DCode(TwoDimensionCodeType.PDF417, websiteString, Size2DCode.TINY), + e.PrintLine(), + + e.PrintLine("PDF417 (LARGE):"), + e.Print2DCode(TwoDimensionCodeType.PDF417, websiteString, Size2DCode.LARGE), + e.PrintLine(), + + e.PrintLine("QRCODE MODEL 1:"), + e.Print2DCode(TwoDimensionCodeType.QRCODE_MODEL1, websiteString), + e.PrintLine(), + + e.PrintLine("QRCODE MODEL 2:"), + e.PrintQRCode(websiteString), + e.PrintLine(), + + e.PrintLine("QRCODE MICRO:"), + e.Print2DCode(TwoDimensionCodeType.QRCODE_MICRO, "github.com/lukevp"), + e.PrintLine(), + + e.PrintLine("QRCODE MODEL 1 (TINY):"), + e.Print2DCode(TwoDimensionCodeType.QRCODE_MODEL1, websiteString, Size2DCode.TINY), + e.PrintLine(), + + e.PrintLine("QRCODE MODEL 1 (LARGE):"), + e.Print2DCode(TwoDimensionCodeType.QRCODE_MODEL1, websiteString, Size2DCode.LARGE), + e.PrintLine() + }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestBarcodeStyles.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestBarcodeStyles.cs new file mode 100644 index 0000000..4af37f6 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestBarcodeStyles.cs @@ -0,0 +1,64 @@ +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; + +namespace ESCPOS_NET.ConsoleTest +{ + + public static partial class Tests + { + public static byte[][] BarcodeStyles(ICommandEmitter e) => new byte[][] { + //TODO: test all widths and put bar in front in label + e.PrintLine("Thinnest Width:"), + e.SetBarcodeHeightInDots(300), + e.SetBarWidth(BarWidth.Thinnest), + e.PrintBarcode(BarcodeType.UPC_A, "012345678905"), + + e.PrintLine("Thin Width:"), + e.SetBarcodeHeightInDots(300), + e.SetBarWidth(BarWidth.Thin), + e.PrintBarcode(BarcodeType.UPC_A, "012345678905"), + + e.PrintLine("Default Width:"), + e.SetBarcodeHeightInDots(300), + e.SetBarWidth(BarWidth.Default), + e.PrintBarcode(BarcodeType.UPC_A, "012345678905"), + + e.PrintLine("Thicker Width:"), + e.SetBarcodeHeightInDots(300), + e.SetBarWidth(BarWidth.Thick), + e.PrintBarcode(BarcodeType.UPC_A, "012345678905"), + + e.PrintLine("Thickest Width:"), + e.SetBarcodeHeightInDots(300), + e.SetBarWidth(BarWidth.Thickest), + e.PrintBarcode(BarcodeType.UPC_A, "012345678905"), + + + e.PrintLine("Short (50 dots):"), + e.SetBarcodeHeightInDots(50), + e.SetBarWidth(BarWidth.Default), + e.PrintBarcode(BarcodeType.UPC_A, "012345678905"), + + e.PrintLine("Tall (255 dots):"), + e.SetBarcodeHeightInDots(255), + e.PrintBarcode(BarcodeType.UPC_A, "012345678905"), + + e.PrintLine("Label Above:"), + e.SetBarcodeHeightInDots(50), + e.SetBarLabelPosition(BarLabelPrintPosition.Above), + e.PrintBarcode(BarcodeType.UPC_A, "012345678905"), + + e.PrintLine("Label Above and Below:"), + e.SetBarLabelPosition(BarLabelPrintPosition.Both), + e.PrintBarcode(BarcodeType.UPC_A, "012345678905"), + + e.PrintLine("Label Below:"), + e.SetBarLabelPosition(BarLabelPrintPosition.Below), + e.PrintBarcode(BarcodeType.UPC_A, "012345678905"), + + e.PrintLine("Font B Label Below:"), + e.SetBarLabelFontB(true), + e.PrintBarcode(BarcodeType.UPC_A, "012345678905") + }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestBarcodeTypes.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestBarcodeTypes.cs new file mode 100644 index 0000000..f1561b0 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestBarcodeTypes.cs @@ -0,0 +1,74 @@ +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; + +namespace ESCPOS_NET.ConsoleTest +{ + + public static partial class Tests + { + public static byte[][] BarcodeTypes(ICommandEmitter e) => new byte[][] { + e.SetBarcodeHeightInDots(600), + e.SetBarWidth(BarWidth.Thinnest), + e.SetBarLabelPosition(BarLabelPrintPosition.Below), + + e.PrintLine("CODABAR_NW_7:"), + e.PrintBarcode(BarcodeType.CODABAR_NW_7, "A31117013206375B"), + + e.PrintLine("CODE128:"), + e.PrintBarcode(BarcodeType.CODE128, "ESC_POS_NET"), + e.PrintLine(), + + e.PrintLine("CODE128 Type C:"), + e.PrintBarcode(BarcodeType.CODE128, "123456789101", BarcodeCode.CODE_C), + e.PrintLine(), + + e.PrintLine("CODE39:"), + e.PrintBarcode(BarcodeType.CODE39, "*ESC-POS-NET*"), + e.PrintLine(), + + e.PrintLine("CODE93:"), + e.PrintBarcode(BarcodeType.CODE93, "*ESC_POS_NET*"), + e.PrintLine(), + + e.PrintLine("GS1_128:"), + e.PrintBarcode(BarcodeType.GS1_128, "(01)9501234567890*"), + e.PrintLine(), + + e.PrintLine("GS1_DATABAR_EXPANDED:"), + e.PrintBarcode(BarcodeType.GS1_DATABAR_EXPANDED, "0001234567890"), + e.PrintLine(), + + e.PrintLine("GS1_DATABAR_LIMITED:"), + e.PrintBarcode(BarcodeType.GS1_DATABAR_LIMITED, "0001234567890"), + e.PrintLine(), + + e.PrintLine("GS1_DATABAR_OMNIDIRECTIONAL:"), + e.PrintBarcode(BarcodeType.GS1_DATABAR_OMNIDIRECTIONAL, "0001234567890"), + e.PrintLine(), + + e.PrintLine("GS1_DATABAR_TRUNCATED:"), + e.PrintBarcode(BarcodeType.GS1_DATABAR_TRUNCATED, "0001234567890"), + e.PrintLine(), + + e.PrintLine("ITF:"), + e.PrintBarcode(BarcodeType.ITF, "1234567895"), + e.PrintLine(), + + e.PrintLine("JAN13_EAN13:"), + e.PrintBarcode(BarcodeType.JAN13_EAN13, "5901234123457"), + e.PrintLine(), + + e.PrintLine("JAN8_EAN8:"), + e.PrintBarcode(BarcodeType.JAN8_EAN8, "96385074"), + e.PrintLine(), + + e.PrintLine("UPC_A:"), + e.PrintBarcode(BarcodeType.UPC_A, "042100005264"), + e.PrintLine(), + + e.PrintLine("UPC_E:"), + e.PrintBarcode(BarcodeType.UPC_E, "425261"), + e.PrintLine() + }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestCashDrawer.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestCashDrawer.cs new file mode 100644 index 0000000..fdbcb7b --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestCashDrawer.cs @@ -0,0 +1,16 @@ +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; + +namespace ESCPOS_NET.ConsoleTest +{ + public static partial class Tests + { + public static byte[][] CashDrawerOpenPin2(ICommandEmitter e) => new byte[][] { + e.CashDrawerOpenPin2() + }; + + public static byte[][] CashDrawerOpenPin5(ICommandEmitter e) => new byte[][] { + e.CashDrawerOpenPin5() + }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestCodePages.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestCodePages.cs new file mode 100644 index 0000000..9eca6af --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestCodePages.cs @@ -0,0 +1,45 @@ +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; +using System.Collections.Generic; +using System.IO; + +namespace ESCPOS_NET.ConsoleTest +{ + public static partial class Tests + { + public static byte[][] CodePages(ICommandEmitter e, CodePage codePage) { + List test = new List(); + test.Add(e.LeftAlign()); + test.Add(e.PrintLine("Empty space = space or non-printable character.")); + test.Add(e.PrintLine("Each row represents the first hex digit.")); + test.Add(e.PrintLine("Each column represents the second hex digit.")); + test.Add(e.PrintLine("For example, row 7, column A, represents")); + test.Add(e.PrintLine("the character with hex 0x7A.")); + test.Add(e.PrintLine()); + test.Add(e.PrintLine("Character Table for Code Page:")); + test.Add(e.PrintLine(codePage + " (Page " + ((int)codePage) + ")")); + test.Add(e.PrintLine("==========================")); + test.Add(e.PrintLine()); + test.Add(e.PrintLine(" 0 1 2 3 4 5 6 7 8 9 A B C D E F ")); + // Set CodePage to test codepage. + test.Add(e.CodePage(codePage)); + for (int d1 = 0; d1 < 0x10; d1++) + { + + var upperDigit = d1.ToString("x1").ToUpperInvariant(); + test.Add(e.Print(upperDigit + " ")); + for (int d2 = 0; d2 < 0x10; d2++) + { + var digit = d1 * 0x10 + d2; + if (digit <= 0x20) digit = 0x20; + test.Add(e.Print((char)digit + " ")); + } + test.Add(e.PrintLine()); + } + // Set CodePage back to default + test.Add(e.CodePage(CodePage.PC437_USA_STANDARD_EUROPE_DEFAULT)); + test.Add(e.PrintLine()); + return test.ToArray(); + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestImages.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestImages.cs new file mode 100644 index 0000000..af7bab8 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestImages.cs @@ -0,0 +1,66 @@ +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; +using System.IO; + +namespace ESCPOS_NET.ConsoleTest +{ + public static partial class Tests + { + public static byte[][] Images(ICommandEmitter e, bool isLegacy) => new byte[][] { + e.CenterAlign(), + e.PrintLine("Test PNG images with widths 100 - 600 px,"), + e.PrintLine("at native resolution"), + e.PrintLine("-- pd-logo-100.png --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-100.png"), true, isLegacy), + e.PrintLine("-- pd-logo-200.png --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-200.png"), true, isLegacy), + e.PrintLine("-- pd-logo-300.png --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy), + e.PrintLine("-- pd-logo-400.png --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-400.png"), true, isLegacy), + e.PrintLine("-- pd-logo-500.png --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-500.png"), true, isLegacy), + e.PrintLine("-- pd-logo-600.png --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-600.png"), true, isLegacy), + e.PrintLine("Test resizing 600 px image to 300px"), + e.PrintLine("-- pd-logo-600.png /300 --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-600.png"), true, isLegacy, 300), + e.PrintLine("Test image with taller height than width"), + e.PrintLine("-- abe-lincoln.png /300 --"), + e.PrintImage(File.ReadAllBytes("images/abe-lincoln.png"), true, isLegacy, 300), + e.PrintLine("Test 300px image resized to all remainders of 8,"), + e.PrintLine("to ensure overflow checking"), + e.PrintLine("-- pd-logo-300.png /296 (0 remainder) --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 296), + e.PrintLine("-- pd-logo-300.png /297 (1 remainder) --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 297), + e.PrintLine("-- pd-logo-300.png /298 (2 remainder) --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 298), + e.PrintLine("-- pd-logo-300.png /299 (3 remainder) --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 299), + e.PrintLine("-- pd-logo-300.png /300 (4 remainder) --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 300), + e.PrintLine("-- pd-logo-300.png /301 (5 remainder) --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 301), + e.PrintLine("-- pd-logo-300.png /302 (6 remainder) --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 302), + e.PrintLine("-- pd-logo-300.png /303 (7 remainder) --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true, isLegacy, 303), + e.PrintLine("Test 300px image in JPG format"), + e.PrintLine("-- pd-logo-300.jpg --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.jpg"), true, isLegacy), + e.PrintLine("Test 300px image in BMP format"), + e.PrintLine("-- pd-logo-300.jpg --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.bmp"), true, isLegacy), + e.PrintLine("Test 300px image in GIF format"), + e.PrintLine("-- pd-logo-300.jpg --"), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.gif"), true, isLegacy), + e.PrintLine("Test full color image converted"), + e.PrintLine("to black and white and interpolated"), + e.PrintLine("-- kitten.jpg --"), + e.PrintImage(File.ReadAllBytes("images/kitten.jpg"), true, isLegacy, 500), + e.PrintLine("-- portal companion cube.jpg --"), + e.PrintImage(File.ReadAllBytes("images/Portal_Companion_Cube.jpg"), true, isLegacy, 500) + }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestLargeByteArrays.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestLargeByteArrays.cs new file mode 100644 index 0000000..aa7b6a4 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestLargeByteArrays.cs @@ -0,0 +1,105 @@ +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; +using System; +using System.IO; + +namespace ESCPOS_NET.ConsoleTest +{ + public static partial class Tests + { + public static byte[] TestLargeByteArrays(ICommandEmitter e) + { + var kitten = e.PrintImage(File.ReadAllBytes("images/kitten.jpg"), true, true, 500); + var cube = e.PrintImage(File.ReadAllBytes("images/Portal_Companion_Cube.jpg"), true, true, 500); + var expectedResult = ByteSplicer.Combine( + e.CenterAlign(), + kitten, + cube, + kitten, + cube, + kitten, + cube, + kitten, + cube, + kitten, + cube, + kitten, + cube, + kitten, + cube, + kitten, + cube, + kitten, + cube, + kitten, + cube + ); + MemoryPrinter mp = new MemoryPrinter(); + mp.Write(expectedResult); + var response = mp.GetAllData(); + bool hasErrors = false; + if (expectedResult.Length != response.Length) + { + Console.WriteLine($"Error: MemoryPrinter length mismatch - ${response.Length}, expected ${expectedResult.Length}"); + hasErrors = true; + } + else + { + for (int i = 0; i < expectedResult.Length; i++) + { + if (expectedResult[i] != response[i]) + { + Console.WriteLine($"Error: MemoryPrinter data mismatch - ${expectedResult[i]}, expected ${response[i]}, at location ${i}"); + hasErrors = true; + } + } + } + if (!hasErrors) + { + Console.WriteLine("MemoryPrinter: ALL OK!"); + } + else + { + Console.WriteLine("MemoryPrinter: Errors occured during testing, aborting!"); + throw new ArgumentException(); + } + + Random r = new Random(); + var filename = $"{r.NextDouble().ToString()}.tmp"; + using (FilePrinter fp = new FilePrinter(filename, true)) + { + fp.Write(expectedResult); + } + response = File.ReadAllBytes(filename); + hasErrors = false; + if (expectedResult.Length != response.Length) + { + Console.WriteLine($"Error: FilePrinter length mismatch - ${response.Length}, expected ${expectedResult.Length}"); + hasErrors = true; + } + else + { + for (int i = 0; i < expectedResult.Length; i++) + { + if (expectedResult[i] != response[i]) + { + Console.WriteLine($"Error: FilePrinter data mismatch - ${expectedResult[i]}, expected ${response[i]}, at location ${i}"); + hasErrors = true; + } + } + } + + if (!hasErrors) + { + Console.WriteLine("FilePrinter: ALL OK!"); + } + else + { + Console.WriteLine("FilePrinter: Errors occured during testing, aborting!"); + throw new ArgumentException(); + } + + return expectedResult; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestLineSpacing.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestLineSpacing.cs new file mode 100644 index 0000000..36ce38b --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestLineSpacing.cs @@ -0,0 +1,18 @@ +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; + +namespace ESCPOS_NET.ConsoleTest +{ + public static partial class Tests + { + public static byte[][] LineSpacing(ICommandEmitter e) => new byte[][] { + e.SetLineSpacingInDots(200), + e.PrintLine("This is the default spacing."), + e.SetLineSpacingInDots(15), + e.PrintLine("This has 200 dots of spacing above."), + e.ResetLineSpacing(), + e.PrintLine("This has 15 dots of spacing above."), + e.PrintLine("This has the default spacing.") + }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestPrinting.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestPrinting.cs new file mode 100644 index 0000000..33c63a6 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestPrinting.cs @@ -0,0 +1,25 @@ +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; + +namespace ESCPOS_NET.ConsoleTest +{ + public static partial class Tests + { + public static byte[][] MultiLinePrinting(ICommandEmitter e) => new byte[][] { + e.Print("Multiline Test: Windows...\r\nOSX...\rUnix...\n"), + //TODO: sanitize test. + e.PrintLine("Feeding 250 dots."), + e.FeedDots(250), + e.PrintLine("Feeding 3 lines."), + e.FeedLines(3), + e.PrintLine("Done Feeding."), + e.PrintLine("Reverse Feeding 6 lines."), + e.FeedLinesReverse(6), + e.PrintLine("Done Reverse Feeding.") + }; + + public static byte[][] SingleLinePrinting(ICommandEmitter e) => new byte[][] { + e.Print("Single Test Line Of Text\r\n"), + }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestReceipt.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestReceipt.cs new file mode 100644 index 0000000..ea8dece --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestReceipt.cs @@ -0,0 +1,51 @@ +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; +using System.IO; + +namespace ESCPOS_NET.ConsoleTest +{ + public static partial class Tests + { + public static byte[][] Receipt(ICommandEmitter e) => new byte[][] { + e.CenterAlign(), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true), + e.PrintLine(), + e.SetBarcodeHeightInDots(360), + e.SetBarWidth(BarWidth.Default), + e.SetBarLabelPosition(BarLabelPrintPosition.None), + e.PrintBarcode(BarcodeType.ITF, "0123456789"), + e.PrintLine("B&H PHOTO & VIDEO"), + e.PrintLine("420 NINTH AVE."), + e.PrintLine("NEW YORK, NY 10001"), + e.PrintLine("(212) 502-6380 - (800)947-9975"), + e.SetStyles(PrintStyle.Underline), + e.PrintLine("www.bhphotovideo.com"), + e.SetStyles(PrintStyle.None), + e.PrintLine(), + e.LeftAlign(), + e.PrintLine("Order: 123456789 Date: 02/01/19"), + e.PrintLine(), + e.PrintLine(), + e.SetStyles(PrintStyle.FontB), + e.PrintLine("1 TRITON LOW-NOISE IN-LINE MICROPHONE PREAMP"), + e.PrintLine(" TRFETHEAD/FETHEAD 89.95 89.95"), + e.PrintLine("----------------------------------------------------------------"), + e.RightAlign(), + e.PrintLine("SUBTOTAL 89.95"), + e.PrintLine("Total Order: 89.95"), + e.PrintLine("Total Payment: 89.95"), + e.PrintLine(), + e.LeftAlign(), + e.SetStyles(PrintStyle.Bold | PrintStyle.FontB), + e.PrintLine("SOLD TO: SHIP TO:"), + e.SetStyles(PrintStyle.FontB), + e.PrintLine(" LUKE PAIREEPINART LUKE PAIREEPINART"), + e.PrintLine(" 123 FAKE ST. 123 FAKE ST."), + e.PrintLine(" DECATUR, IL 12345 DECATUR, IL 12345"), + e.PrintLine(" (123)456-7890 (123)456-7890"), + e.PrintLine(" CUST: 87654321"), + e.PrintLine(), + e.PrintLine() + }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestTextStyles.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestTextStyles.cs new file mode 100644 index 0000000..5b28ab1 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/TestTextStyles.cs @@ -0,0 +1,41 @@ +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; + +namespace ESCPOS_NET.ConsoleTest +{ + + public static partial class Tests + { + public static byte[][] TextStyles(ICommandEmitter e) => new byte[][] { + e.SetStyles(PrintStyle.None), + e.Print("Default: The quick brown fox jumped over the lazy dogs.\n"), + e.SetStyles(PrintStyle.FontB), + e.Print("Font B: The quick brown fox jumped over the lazy dogs.\n"), + e.SetStyles(PrintStyle.Bold), + e.Print("Bold: The quick brown fox jumped over the lazy dogs.\n"), + e.SetStyles(PrintStyle.Underline), + e.Print("Underline: The quick brown fox jumped over the lazy dogs.\n"), + e.SetStyles(PrintStyle.DoubleWidth), + e.Print("DoubleWidth: The quick brown fox jumped over the lazy dogs.\n"), + e.SetStyles(PrintStyle.DoubleHeight), + e.Print("DoubleHeight: The quick brown fox jumped over the lazy dogs.\n"), + e.SetStyles(PrintStyle.FontB | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.Underline | PrintStyle.Bold), + e.Print("All Styles: The quick brown fox jumped over the lazy dogs.\n"), + e.SetStyles(PrintStyle.None), + e.ReverseMode(true), + e.PrintLine("REVERSE MODE: The quick brown fox jumped over the lazy dogs."), + e.SetStyles(PrintStyle.FontB | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth), + e.PrintLine("REVERSE MODE: The quick brown fox jumped over the lazy dogs."), + e.SetStyles(PrintStyle.None), + e.ReverseMode(false), + e.SetStyles(PrintStyle.None), + e.RightCharacterSpacing(5), + e.PrintLine("Right space 5: The quick brown fox jumped over the lazy dogs."), + e.RightCharacterSpacing(0), + e.SetStyles(PrintStyle.None), + e.UpsideDownMode(true), + e.PrintLine("Upside Down Mode: The quick brown fox jumped over the lazy dogs."), + e.UpsideDownMode(false) + }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/Portal_Companion_Cube.jpg b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/Portal_Companion_Cube.jpg new file mode 100644 index 0000000..9b965a9 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/Portal_Companion_Cube.jpg differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/abe-lincoln.png b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/abe-lincoln.png new file mode 100644 index 0000000..ebd9580 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/abe-lincoln.png differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/kitten.jpg b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/kitten.jpg new file mode 100644 index 0000000..0142741 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/kitten.jpg differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-100.png b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-100.png new file mode 100644 index 0000000..9374f55 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-100.png differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-200.png b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-200.png new file mode 100644 index 0000000..b81b563 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-200.png differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-300.bmp b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-300.bmp new file mode 100644 index 0000000..b2caee2 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-300.bmp differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-300.gif b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-300.gif new file mode 100644 index 0000000..9c0e087 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-300.gif differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-300.jpg b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-300.jpg new file mode 100644 index 0000000..8ff7dc3 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-300.jpg differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-300.png b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-300.png new file mode 100644 index 0000000..5ba5f5d Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-300.png differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-400.png b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-400.png new file mode 100644 index 0000000..68de0f4 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-400.png differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-500.png b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-500.png new file mode 100644 index 0000000..fc0ac2d Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-500.png differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-600.png b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-600.png new file mode 100644 index 0000000..81dd47d Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo-600.png differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo.png b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo.png new file mode 100644 index 0000000..528dc28 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo.png differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo.svg b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo.svg new file mode 100644 index 0000000..6339d15 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/images/pd-logo.svg @@ -0,0 +1,172 @@ + + + +image/svg+xmlOpenclipart diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/test-files/output-juliogamasso.bin b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/test-files/output-juliogamasso.bin new file mode 100644 index 0000000..6fa4bc2 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.ConsoleTest/test-files/output-juliogamasso.bin differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.TCPPrintServerTest/ESCPOS_NET.TCPPrintServerTest.csproj b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.TCPPrintServerTest/ESCPOS_NET.TCPPrintServerTest.csproj new file mode 100644 index 0000000..41f1d5a --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.TCPPrintServerTest/ESCPOS_NET.TCPPrintServerTest.csproj @@ -0,0 +1,8 @@ + + + + Exe + net6.0 + + + diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.TCPPrintServerTest/Program.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.TCPPrintServerTest/Program.cs new file mode 100644 index 0000000..053ccce --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.TCPPrintServerTest/Program.cs @@ -0,0 +1,40 @@ +// This code is adapted from a sample found at the URL +// "http://blogs.msdn.com/b/jmanning/archive/2004/12/19/325699.aspx" + +using System; +using System.Net; +using System.Net.Sockets; +using System.IO; +using System.Text; + +namespace TcpEchoServer +{ + public class TcpEchoServer + { + public static void Main() + { + Console.WriteLine("Starting echo server..."); + + int port = 9100; + TcpListener listener = new TcpListener(IPAddress.Loopback, port); + listener.Start(); + + TcpClient client = listener.AcceptTcpClient(); + NetworkStream stream = client.GetStream(); + StreamWriter writer = new StreamWriter(stream, Encoding.ASCII) { AutoFlush = true }; + StreamReader reader = new StreamReader(stream, Encoding.ASCII); + + while (true) + { + string inputLine = ""; + while (inputLine != null) + { + inputLine = reader.ReadLine(); + writer.Write("E"); + Console.WriteLine("Echoing string: " + inputLine); + } + Console.WriteLine("Server saw disconnect from client."); + } + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.UnitTest/ESCPOS_NET.UnitTest.csproj b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.UnitTest/ESCPOS_NET.UnitTest.csproj new file mode 100644 index 0000000..9dda58a --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.UnitTest/ESCPOS_NET.UnitTest.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.UnitTest/EmittersBased/EPSONTests/BarCode.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.UnitTest/EmittersBased/EPSONTests/BarCode.cs new file mode 100644 index 0000000..a54075a --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.UnitTest/EmittersBased/EPSONTests/BarCode.cs @@ -0,0 +1,77 @@ +using ESCPOS_NET.Emitters; +using Xunit; + +namespace ESCPOS_NET.UnitTest.EmittersBased.EPSONTests +{ + public class BarCode + { + private const string WEBSITE_STRING = "https://github.com/lukevp/ESC-POS-.NET/"; + + [Theory] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL1, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_15)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_25)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_30)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.EXTRA, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.LARGE, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.NORMAL, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.SMALL, CorrectionLevel2DCode.PERCENT_7)] + [InlineData(TwoDimensionCodeType.QRCODE_MICRO, Size2DCode.TINY, CorrectionLevel2DCode.PERCENT_7)] + public void PrintQRCode_Success(TwoDimensionCodeType codeType, Size2DCode size2DCode, CorrectionLevel2DCode correctionLevel2DCode) + { + var result = new byte[] { 29, 40, 107, 4, 0, 49, 65, (byte)codeType, 0, 29, 40, 107, 3, 0, 49, 67, (byte)size2DCode, 29, 40, 107, 3, 0, 49, 69, (byte)correctionLevel2DCode, 29, 40, 107, 42, 0, 49, 80, 48, 104, 116, 116, 112, 115, 58, 47, 47, 103, 105, 116, 104, 117, 98, 46, 99, 111, 109, 47, 108, 117, 107, 101, 118, 112, 47, 69, 83, 67, 45, 80, 79, 83, 45, 46, 78, 69, 84, 47, 29, 40, 107, 3, 0, 49, 81, 48 }; + Assert.Equal(result, new EPSON().Print2DCode(codeType, WEBSITE_STRING, size2DCode, correctionLevel2DCode)); + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.UnitTest/GlobalTests/Barcode.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.UnitTest/GlobalTests/Barcode.cs new file mode 100644 index 0000000..bbde0b9 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.UnitTest/GlobalTests/Barcode.cs @@ -0,0 +1,40 @@ +using ESCPOS_NET.Emitters; +using System; +using System.Reflection; +using Xunit; + +namespace ESCPOS_NET.UnitTest.GlobalTests +{ + public class Barcode + { + private const string WEBSITE_STRING = "https://github.com/lukevp/ESC-POS-.NET/"; + + [Theory] + [InlineData("EPSON", TwoDimensionCodeType.QRCODE_MODEL1)] + [InlineData("EPSON", TwoDimensionCodeType.QRCODE_MODEL2)] + [InlineData("EPSON", TwoDimensionCodeType.QRCODE_MICRO)] + [InlineData("EPSON", null)] + public void PrintQRCode_Success(string emitter, TwoDimensionCodeType? codeType) + { + var type = Assembly.LoadFrom("ESCPOS_NET").GetType($"ESCPOS_NET.Emitters.{emitter}", true); + ICommandEmitter e = (ICommandEmitter)Activator.CreateInstance(type); + + byte[] bytes; + if (codeType is TwoDimensionCodeType codeTypeValue) + bytes = e.PrintQRCode(WEBSITE_STRING, codeTypeValue); + else + bytes = e.PrintQRCode(WEBSITE_STRING); + + Assert.True(bytes.Length > 0); + } + + [Theory] + [InlineData("EPSON", TwoDimensionCodeType.PDF417)] + public void PrintQRCode_Failure(string emitter, TwoDimensionCodeType codeType) + { + var type = Assembly.LoadFrom("ESCPOS_NET").GetType($"ESCPOS_NET.Emitters.{emitter}", true); + ICommandEmitter e = (ICommandEmitter)Activator.CreateInstance(type); + Assert.Throws(() => e.PrintQRCode(WEBSITE_STRING, codeType)); + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.sln b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.sln new file mode 100644 index 0000000..796d8b5 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31515.178 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ESCPOS_NET", "ESCPOS_NET\ESCPOS_NET.csproj", "{BA181101-43A5-435E-8744-ACDA313D04D8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ESCPOS_NET.ConsoleTest", "ESCPOS_NET.ConsoleTest\ESCPOS_NET.ConsoleTest.csproj", "{958464FE-6B30-4BA7-A4B1-D63ED2E23158}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ESCPOS_NET.UnitTest", "ESCPOS_NET.UnitTest\ESCPOS_NET.UnitTest.csproj", "{8A848879-9776-4EBC-8484-41B8EE65FD8D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BA181101-43A5-435E-8744-ACDA313D04D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA181101-43A5-435E-8744-ACDA313D04D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA181101-43A5-435E-8744-ACDA313D04D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA181101-43A5-435E-8744-ACDA313D04D8}.Release|Any CPU.Build.0 = Release|Any CPU + {958464FE-6B30-4BA7-A4B1-D63ED2E23158}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {958464FE-6B30-4BA7-A4B1-D63ED2E23158}.Debug|Any CPU.Build.0 = Debug|Any CPU + {958464FE-6B30-4BA7-A4B1-D63ED2E23158}.Release|Any CPU.ActiveCfg = Release|Any CPU + {958464FE-6B30-4BA7-A4B1-D63ED2E23158}.Release|Any CPU.Build.0 = Release|Any CPU + {8A848879-9776-4EBC-8484-41B8EE65FD8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A848879-9776-4EBC-8484-41B8EE65FD8D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A848879-9776-4EBC-8484-41B8EE65FD8D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A848879-9776-4EBC-8484-41B8EE65FD8D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FB679B5F-BE89-407A-820F-185FE53B15C5} + EndGlobalSection +EndGlobal diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/ByteArrayBuilder.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/ByteArrayBuilder.cs new file mode 100644 index 0000000..6246f2d --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/ByteArrayBuilder.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +/* FROM: https://www.codeproject.com/Tips/674256/ByteArrayBuilder-a-StringBuilder-for-Bytes 2/9/2019 */ +namespace ESCPOS_NET.Utilities +{ + /// + /// Provides similar functionality to a StringBuilder, but for bytes. + /// + /// + /// To fill the builder, construct a new, empty builder, and call the + /// appropriate Append method overloads. + /// To read data from the builder, either use Rewind on an existing + /// builder, or construct a new builder by passing it the byte array + /// from a previous builder - which you can get with the ToArray + /// method. + /// + /// + /// + /// ByteArrayBuilder bab = new ByteArrayBuilder(); + /// string[] lines = File.ReadAllLines(@"D:\Temp\myText.txt"); + /// bab.Append(lines.Length); + /// foreach (string s in lines) + /// { + /// bab.Append(s); + /// } + /// byte[] data = bab.ToArray(); + /// ... + /// ByteArrayBuilder babOut = new ByteArrayBuilder(data); + /// int count = bab.GetInt(); + /// string[] linesOut = new string[count]; + /// for (int lineNo = 0; lineNo < count; lineNo++) + /// { + /// linesOut[lineNo](babOut.GetString()); + /// } + /// . + /// + public class ByteArrayBuilder : IDisposable + { + /// + /// True in a byte form of the Line. + /// + private const byte StreamTrue = (byte)1; + + /// + /// False in the byte form of a line. + /// + private const byte StreamFalse = (byte)0; + + /// + /// Holds the actual bytes. + /// + private MemoryStream store = new MemoryStream(); + + /// + /// Gets bytes in the store. + /// + public int Length => (int)store.Length; + + /// + /// Create a new, empty builder ready to be filled. + /// + public ByteArrayBuilder() + { + } + + public ByteArrayBuilder Append(byte b) + { + AddBytes(new byte[] { b }); + return this; + } + + /// + /// Adds an IEnumerable of bytes to an array. + /// + /// Value to append to existing builder data. + /// + public ByteArrayBuilder Append(IEnumerable b) + { + if (b is byte[]) + { + AddBytes((byte[])b); + return this; + } + + AddBytes(b.ToArray()); + return this; + } + + /// + /// Clear all content from the builder. + /// + public void Clear() + { + store.Close(); + store.Dispose(); + store = new MemoryStream(); + } + + /// + /// Rewind the builder ready to read data. + /// + public void Rewind() + { + store.Seek(0, SeekOrigin.Begin); + } + + /// + /// Set an absolute position in the builder. + /// **WARNING** + /// If you add any variable size objects to the builder, the results of + /// reading after a Seek to a non-zero value are unpredictable. + /// A builder does not store just objects - for some it stores additional + /// information as well. + /// + /// the position to seek. + public void Seek(int position) + { + store.Seek((long)position, SeekOrigin.Begin); + } + + /// + /// Returns the builder as an array of bytes. + /// + /// An array. + public byte[] ToArray() + { + byte[] data = new byte[Length]; + Array.Copy(store.GetBuffer(), data, Length); + return data; + } + + /// + /// Returns a text based (Base64) string version of the current content. + /// + /// The converted string. + public override string ToString() + { + return Convert.ToBase64String(ToArray()); + } + + /// + /// Add a string of raw bytes to the store. + /// + /// the byte array. + private void AddBytes(byte[] byteArray) + { + store.Write(byteArray, 0, byteArray.Length); + } + + /// + /// Reads a specific number of bytes from the store. + /// + /// The length. + /// The byte array. + private byte[] GetBytes(int length) + { + byte[] data = new byte[length]; + if (length > 0) + { + int read = store.Read(data, 0, length); + if (read != length) + { + throw new ApplicationException("Buffer did not contain " + length + " bytes"); + } + } + + return data; + } + + /// + /// Dispose of this builder and its resources. + /// + public void Dispose() + { + store.Close(); + store.Dispose(); + } + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/ByteExtensions.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/ByteExtensions.cs new file mode 100644 index 0000000..3f73a5e --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/ByteExtensions.cs @@ -0,0 +1,15 @@ +namespace ESCPOS_NET +{ + public static class ByteExtensions + { + public static bool IsBitSet(this byte b, int offset) + { + return (b & (1 << offset)) != 0; + } + + public static bool IsBitNotSet(this byte b, int offset) + { + return (b & (1 << offset)) == 0; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/ByteSplicer.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/ByteSplicer.cs new file mode 100644 index 0000000..fa2f694 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/ByteSplicer.cs @@ -0,0 +1,15 @@ +namespace ESCPOS_NET.Utilities +{ + public static class ByteSplicer + { + public static byte[] Combine(params byte[][] byteArrays) + { + ByteArrayBuilder builder = new ByteArrayBuilder(); + foreach (var byteArray in byteArrays) + { + builder.Append(byteArray); + } + return builder.ToArray(); + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/DataValidation/DataConstraint.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/DataValidation/DataConstraint.cs new file mode 100644 index 0000000..745ca70 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/DataValidation/DataConstraint.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace ESCPOS_NET.DataValidation +{ + public class DataConstraint + { + public int MinLength { get; set; } + + public int MaxLength { get; set; } + + public List ValidLengths { get; set; } + + public string ValidChars { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/DataValidation/DataValidator.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/DataValidation/DataValidator.cs new file mode 100644 index 0000000..e8d021d --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/DataValidation/DataValidator.cs @@ -0,0 +1,193 @@ +using ESCPOS_NET.Emitters; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ESCPOS_NET.DataValidation +{ + public abstract class BaseDataValidator where T : Enum + { + protected Dictionary _constraints; + + public void Validate(T type, string data, BarcodeCode? code = null) + { + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } + + // Validate constraints on barcode. + _constraints.TryGetValue(type, out var constraints); + if (constraints is null) + { + return; + } + + // Check lengths + if (constraints.ValidLengths != null) + { + if (!constraints.ValidLengths.Contains(data.Length)) + { + throw new ArgumentException($"Code '{data}' is not a valid length from: [{string.Join(", ", constraints.ValidLengths)}]."); + } + } + else if (data.Length < constraints.MinLength) + { + throw new ArgumentException($"Code '{data}' is shorter than minimum length {constraints.MinLength}."); + } + else if (data.Length > constraints.MaxLength) + { + throw new ArgumentException($"Code '{data}' is longer than maximum length {constraints.MaxLength}."); + } + + // Check if barcode contains invalid characters. + if (constraints.ValidChars == "7BIT-ASCII") + { + if (!data.All(x => x <= 127 && x >= 0)) + { + throw new ArgumentException($"Code '{data}' contained invalid characters not in: {constraints.ValidChars}."); + } + } + else if (constraints.ValidChars != null && !data.All(x => constraints.ValidChars.Contains(x))) + { + throw new ArgumentException($"Code '{data}' contained invalid characters not in: {constraints.ValidChars}."); + } + + RunSpecificValidations(type, data, code); + } + + protected abstract void RunSpecificValidations(T type, string data, BarcodeCode? code); + } + + public static class DataValidator + { + private static BarcodeDataValidator singletonBarcode = null; + private static TwoDimensionCodeDataValidator singleton2DCode = null; + + public static void ValidateBarcode(BarcodeType type, BarcodeCode code, string data) + { + if (singletonBarcode is null) + { + singletonBarcode = new BarcodeDataValidator(); + } + + singletonBarcode.Validate(type, data, code); + } + + public static void Validate2DCode(TwoDimensionCodeType type, string data) + { + if (singleton2DCode is null) + { + singleton2DCode = new TwoDimensionCodeDataValidator(); + } + + singleton2DCode.Validate(type, data); + } + + private class BarcodeDataValidator : BaseDataValidator + { + public BarcodeDataValidator() + { + _constraints = new Dictionary() + { + { BarcodeType.UPC_A, new DataConstraint() { MinLength = 11, MaxLength = 12, ValidChars = "0123456789" } }, + { BarcodeType.UPC_E, new DataConstraint() { ValidLengths = new List() { 6, 7, 8, 11, 12 }, ValidChars = "0123456789" } }, + { BarcodeType.JAN13_EAN13, new DataConstraint() { MinLength = 12, MaxLength = 13, ValidChars = "0123456789" } }, + { BarcodeType.JAN8_EAN8, new DataConstraint() { MinLength = 7, MaxLength = 8, ValidChars = "0123456789" } }, + { BarcodeType.CODE39, new DataConstraint() { MinLength = 1, MaxLength = 255, ValidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./" } }, + { BarcodeType.ITF, new DataConstraint() { MinLength = 2, MaxLength = 255, ValidChars = "0123456789" } }, + { BarcodeType.CODABAR_NW_7, new DataConstraint() { MinLength = 2, MaxLength = 255, ValidChars = "0123456789ABCDabcd$+-./:" } }, + { BarcodeType.CODE93, new DataConstraint() { MinLength = 1, MaxLength = 255, ValidChars = "7BIT-ASCII" } }, + { BarcodeType.CODE128, new DataConstraint() { MinLength = 1, MaxLength = 253, ValidChars = "7BIT-ASCII" } }, + { BarcodeType.GS1_128, new DataConstraint() { MinLength = 1, MaxLength = 253, ValidChars = "7BIT-ASCII" } }, + { BarcodeType.GS1_DATABAR_OMNIDIRECTIONAL, new DataConstraint() { MinLength = 13, MaxLength = 13, ValidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./" } }, + { BarcodeType.GS1_DATABAR_TRUNCATED, new DataConstraint() { MinLength = 13, MaxLength = 13, ValidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./" } }, + { BarcodeType.GS1_DATABAR_LIMITED, new DataConstraint() { MinLength = 13, MaxLength = 13, ValidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./" } }, + { BarcodeType.GS1_DATABAR_EXPANDED, new DataConstraint() { MinLength = 2, MaxLength = 255, ValidChars = "0123456789ABCDabcd !\"%$'()*+,-./:;<=>?_{" } }, + }; + } + + protected override void RunSpecificValidations(BarcodeType type, string barcode, BarcodeCode? code) + { + switch (type) + { + case BarcodeType.UPC_E: + if (barcode.Length != 6 && !barcode.StartsWith("0", StringComparison.InvariantCulture)) + { + throw new ArgumentException($"UPC_E Barcode {barcode} with length of 7, 8, 11, or 12 must start with 0."); + } + + break; + + case BarcodeType.ITF: + if (barcode.Length % 2 != 0) + { + throw new ArgumentException($"ITF Barcode {barcode} has length {barcode.Length}, which is not an even number."); + } + + break; + + case BarcodeType.CODABAR_NW_7: + if (!"ABCD".Contains(barcode[0]) || !"ABCD".Contains(barcode[barcode.Length - 1])) + { + throw new ArgumentException($"CODABAR_NW_7 Barcode {barcode} must start and end with an ABCD character."); + } + + if (barcode.Skip(1).Take(barcode.Length - 2).Any(x => "ABCD".Contains(x))) + { + throw new ArgumentException($"CODABAR_NW_7 Barcode {barcode} must not include ABCD characters in the body of the barcode."); + } + + break; + + case BarcodeType.CODE93: + if (!barcode.StartsWith("*", StringComparison.InvariantCulture) || !barcode.EndsWith("*", StringComparison.InvariantCulture)) + { + throw new ArgumentException($"CODE93 Barcode {barcode} must start and end with * characters."); + } + + break; + + case BarcodeType.CODE128: + if (code == BarcodeCode.CODE_C) + { + if (barcode.Length % 2 != 0) + { + throw new ArgumentException($"{nameof(barcode)} length must be divisible by 2"); + } + + if (!barcode.All(x => x <= '9' && x >= '0')) + { + throw new ArgumentException($"Barcode {barcode} is invalid. CODE128 CODE_C barcodes only support numeric characters."); + } + } + + break; + } + } + } + + private class TwoDimensionCodeDataValidator : BaseDataValidator + { + public TwoDimensionCodeDataValidator() + { + _constraints = new Dictionary() + { + { TwoDimensionCodeType.PDF417, new DataConstraint() { MinLength = 0, MaxLength = 255 } }, + { TwoDimensionCodeType.QRCODE_MODEL1, new DataConstraint() { MinLength = 0, MaxLength = 707 } }, + { TwoDimensionCodeType.QRCODE_MODEL2, new DataConstraint() { MinLength = 0, MaxLength = 4296 } }, + { TwoDimensionCodeType.QRCODE_MICRO, new DataConstraint() { MinLength = 0, MaxLength = 21 } }, + }; + } + + // TODO: Research specific validations for QRCode & PDF417 + protected override void RunSpecificValidations(TwoDimensionCodeType type, string barcode, BarcodeCode? code) + { + if (code != null) + { + throw new ArgumentException($"Barcode code should be always null for 2D Codes.", nameof(code)); + } + } + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/ESCPOS_NET.csproj b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/ESCPOS_NET.csproj new file mode 100644 index 0000000..7f90e86 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/ESCPOS_NET.csproj @@ -0,0 +1,33 @@ + + + netstandard2.0 + false + true + animalistic.io + ESCPOS_NET + Luke Paireepinart + .NET Standard 2.0 Implementation of ESC/POS command generation and integration with thermal printers. +Allows creating receipts with all common functionality supported. + Copyright 2021 CandL Development, LLC. + + https://github.com/lukevp/ESC-POS-.NET + git + epson thermal receipt printing printer usb wifi bluetooth serial esc pos escpos escp esc/pos esc/pos.net esc_pos esc_pos_net esc_pos_usb esc_pos_usb_net + 2.0.0 + 2.0.0.0 + 2.0.0.0 + MIT + https://github.com/lukevp/ESC-POS-.NET + - Completely rebuilt the network printer code + + + + + + + + + + + + diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/ActionCommands.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/ActionCommands.cs new file mode 100644 index 0000000..e713659 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/ActionCommands.cs @@ -0,0 +1,16 @@ +using ESCPOS_NET.Emitters.BaseCommandValues; + +namespace ESCPOS_NET.Emitters +{ + public abstract partial class BaseCommandEmitter : ICommandEmitter + { + /* Action Commands */ + public virtual byte[] FullCut() => new byte[] { Cmd.GS, Ops.PaperCut, Functions.PaperCutFullCut }; + + public virtual byte[] PartialCut() => new byte[] { Cmd.GS, Ops.PaperCut, Functions.PaperCutPartialCut }; + + public virtual byte[] FullCutAfterFeed(int lineCount) => new byte[] { Cmd.GS, Ops.PaperCut, Functions.PaperCutFullCutWithFeed, (byte)lineCount }; + + public virtual byte[] PartialCutAfterFeed(int lineCount) => new byte[] { Cmd.GS, Ops.PaperCut, Functions.PaperCutPartialCutWithFeed, (byte)lineCount }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/BarcodeCommands.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/BarcodeCommands.cs new file mode 100644 index 0000000..63f27ae --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/BarcodeCommands.cs @@ -0,0 +1,115 @@ +using ESCPOS_NET.DataValidation; +using ESCPOS_NET.Emitters.BaseCommandValues; +using ESCPOS_NET.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ESCPOS_NET.Emitters +{ + public abstract partial class BaseCommandEmitter : ICommandEmitter + { + /* Barcode Commands */ + public virtual byte[] PrintBarcode(BarcodeType type, string barcode, BarcodeCode code = BarcodeCode.CODE_B) + { + DataValidator.ValidateBarcode(type, code, barcode); + return BarcodeBytes(type, barcode, code); + } + + protected virtual byte[] BarcodeBytes(BarcodeType type, string barcode, BarcodeCode code) + { + // For CODE128, prepend the first 2 characters as 0x7B and the CODE type, and escape 0x7B characters. + if (type == BarcodeType.CODE128) + { + if (code == BarcodeCode.CODE_C) + { + byte[] b = Encoding.ASCII.GetBytes(barcode); + byte[] ob = new byte[b.Length / 2]; + for (int i = 0, obc = 0; i < b.Length; i += 2) + { + ob[obc++] = (byte)(((b[i] - '0') * 10) + (b[i + 1] - '0')); + } + + barcode = Encoding.ASCII.GetString(ob); + } + + barcode = barcode.Replace("{", "{{"); + barcode = $"{(char)0x7B}{(char)code}" + barcode; + } + + var command = new List { Cmd.GS, Barcodes.PrintBarcode, (byte)type, (byte)barcode.Length }; + command.AddRange(barcode.ToCharArray().Select(x => (byte)x)); + return command.ToArray(); + } + + public virtual byte[] PrintQRCode(string data, TwoDimensionCodeType type = TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode size = Size2DCode.NORMAL, CorrectionLevel2DCode correction = CorrectionLevel2DCode.PERCENT_7) + { + if (type == TwoDimensionCodeType.PDF417) + { + throw new ArgumentException($"{nameof(TwoDimensionCodeType.PDF417)} is not a valid QRCode type. Please use {nameof(Print2DCode)} method", nameof(type)); + } + + return Print2DCode(type, data, size, correction); + } + + public virtual byte[] Print2DCode(TwoDimensionCodeType type, string data, Size2DCode size = Size2DCode.NORMAL, CorrectionLevel2DCode correction = CorrectionLevel2DCode.PERCENT_7) + { + DataValidator.Validate2DCode(type, data); + return TwoDimensionCodeBytes(type, data, size, correction); + } + + protected virtual byte[] TwoDimensionCodeBytes(TwoDimensionCodeType type, string data, Size2DCode size, CorrectionLevel2DCode correction) + { + List command = new List(); + byte[] initial = { Cmd.GS, Barcodes.Set2DCode, Barcodes.PrintBarcode }; + switch (type) + { + case TwoDimensionCodeType.PDF417: + command.AddRange(initial, Barcodes.SetPDF417NumberOfColumns, Barcodes.AutoEnding); + command.AddRange(initial, Barcodes.SetPDF417NumberOfRows, Barcodes.AutoEnding); + command.AddRange(initial, Barcodes.SetPDF417DotSize, (byte)size); + command.AddRange(initial, Barcodes.SetPDF417CorrectionLevel, (byte)correction); + + // k = (pL + pH * 256) - 3 --> But pH is always 0. + int k = data.Length; + int l = k + 3; + command.AddRange(initial, (byte)l, Barcodes.StorePDF417Data); + command.AddRange(data.ToCharArray().Select(x => (byte)x)); + + // Prints stored PDF417 + command.AddRange(initial, Barcodes.PrintPDF417); + break; + + case TwoDimensionCodeType.QRCODE_MODEL1: + case TwoDimensionCodeType.QRCODE_MODEL2: + case TwoDimensionCodeType.QRCODE_MICRO: + command.AddRange(initial, Barcodes.SelectQRCodeModel, (byte)type, Barcodes.AutoEnding); + command.AddRange(initial, Barcodes.SetQRCodeDotSize, (byte)size); + command.AddRange(initial, Barcodes.SetQRCodeCorrectionLevel, (byte)correction); + int num = data.Length + 3; + int pL = num % 256; + int pH = num / 256; + command.AddRange(initial, (byte)pL, (byte)pH, Barcodes.StoreQRCodeData); + command.AddRange(data.ToCharArray().Select(x => (byte)x)); + + // Prints stored QRCode + command.AddRange(initial, Barcodes.PrintQRCode); + break; + + default: + throw new NotImplementedException($"2D Code '{type}' was not implemented yet."); + } + + return command.ToArray(); + } + + public virtual byte[] SetBarcodeHeightInDots(int height) => new byte[] { Cmd.GS, Barcodes.SetBarcodeHeightInDots, (byte)height }; + + public virtual byte[] SetBarWidth(BarWidth width) => new byte[] { Cmd.GS, Barcodes.SetBarWidth, (byte)width }; + + public virtual byte[] SetBarLabelPosition(BarLabelPrintPosition position) => new byte[] { Cmd.GS, Barcodes.SetBarLabelPosition, (byte)position }; + + public virtual byte[] SetBarLabelFontB(bool fontB) => new byte[] { Cmd.GS, Barcodes.SetBarLabelFont, (byte)(fontB ? 1 : 0) }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/CashDrawerCommands.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/CashDrawerCommands.cs new file mode 100644 index 0000000..78891b3 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/CashDrawerCommands.cs @@ -0,0 +1,16 @@ +using ESCPOS_NET.Emitters.BaseCommandValues; + +namespace ESCPOS_NET.Emitters +{ + public abstract partial class BaseCommandEmitter : ICommandEmitter + { + /* Cash Drawer Commands */ + public virtual byte[] CashDrawerOpenPin2(int pulseOnTimeMs = 120, int pulseOffTimeMs = 240) { + return new byte[] { Cmd.ESC, Ops.CashDrawerPulse, 0x00, (byte) (pulseOnTimeMs / 2), (byte) (pulseOffTimeMs / 2) }; + } + + public virtual byte[] CashDrawerOpenPin5(int pulseOnTimeMs = 120, int pulseOffTimeMs = 240) { + return new byte[] { Cmd.ESC, Ops.CashDrawerPulse, 0x01, (byte) (pulseOnTimeMs / 2), (byte) (pulseOffTimeMs / 2) }; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/CharacterCommands.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/CharacterCommands.cs new file mode 100644 index 0000000..5dac730 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/CharacterCommands.cs @@ -0,0 +1,22 @@ +using ESCPOS_NET.Emitters.BaseCommandValues; + +namespace ESCPOS_NET.Emitters +{ + public abstract partial class BaseCommandEmitter : ICommandEmitter + { + /* Character Commands */ + public virtual byte[] SetStyles(PrintStyle style) => new byte[] { Cmd.ESC, Chars.StyleMode, (byte)style }; + + public virtual byte[] LeftAlign() => new byte[] { Cmd.ESC, Chars.Alignment, (byte)Align.Left }; + + public virtual byte[] CenterAlign() => new byte[] { Cmd.ESC, Chars.Alignment, (byte)Align.Center }; + + public virtual byte[] RightAlign() => new byte[] { Cmd.ESC, Chars.Alignment, (byte)Align.Right }; + + public virtual byte[] RightCharacterSpacing(int spaceCount) => new byte[] { Cmd.ESC, Chars.RightCharacterSpacing, (byte)spaceCount }; + + public virtual byte[] CodePage(CodePage codePage) => new byte[] { Cmd.ESC, Chars.CodePage, (byte)codePage }; + + public virtual byte[] Color(Color color) => new byte[] { Cmd.ESC, Chars.Color, (byte)color }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/DisplayCommands.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/DisplayCommands.cs new file mode 100644 index 0000000..ba6c170 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/DisplayCommands.cs @@ -0,0 +1,10 @@ +using ESCPOS_NET.Emitters.BaseCommandValues; + +namespace ESCPOS_NET.Emitters +{ + public abstract partial class BaseCommandEmitter : ICommandEmitter + { + /* Display Commands */ + public virtual byte[] Clear() => new byte[] { Display.CLR }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/ImageCommands.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/ImageCommands.cs new file mode 100644 index 0000000..6b247d2 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/ImageCommands.cs @@ -0,0 +1,122 @@ +using ESCPOS_NET.Emitters.BaseCommandValues; +using ESCPOS_NET.Utilities; +using SixLabors.ImageSharp; + +namespace ESCPOS_NET.Emitters +{ + public abstract partial class BaseCommandEmitter : ICommandEmitter + { + private byte[] GetImageHeader(int commandLength) + { + byte[] lengths = new byte[4]; + int i = 0; + while (commandLength > 0) + { + lengths[i] = (byte)(commandLength & 0xFF); + commandLength >>= 8; + i++; + } + + if (i >= 3) + { + return new byte[] { Cmd.GS, Images.ImageCmd8, Images.ImageCmdL, lengths[0], lengths[1], lengths[2], lengths[3] }; + } + else + { + return new byte[] { Cmd.GS, Images.ImageCmdParen, Images.ImageCmdL, lengths[0], lengths[1] }; + } + } + + /* Image Commands */ + public virtual byte[] SetImageDensity(bool isHiDPI) + { + ByteArrayBuilder builder = new ByteArrayBuilder(); + byte dpiSetting = isHiDPI ? (byte)0x33 : (byte)0x32; // TODO: is this right?? + byte[] baseCommand = new byte[] { 0x30, 0x31, dpiSetting, dpiSetting }; + builder.Append(GetImageHeader(baseCommand.Length)); + builder.Append(baseCommand); + return builder.ToArray(); + } + + public virtual byte[] BufferImage(byte[] image, int maxWidth = -1, bool isLegacy = false, int color = 1) + { + ByteArrayBuilder imageCommand = new ByteArrayBuilder(); + + byte colorByte; + switch (color) + { + case 2: + colorByte = 0x32; + break; + case 3: + colorByte = 0x33; + break; + default: + colorByte = 0x31; + break; + } + + int width; + int height; + byte[] imageData; + using (var img = Image.Load(image)) + { + imageData = img.ToSingleBitPixelByteArray(maxWidth: maxWidth == -1 ? (int?)null : maxWidth); + height = img.Height; + width = img.Width; + } + + byte heightL = (byte)height; + byte heightH = (byte)(height >> 8); + + if (isLegacy) + { + var byteWidth = (width + 7 & -8) / 8; + byte widthL = (byte)byteWidth; + byte widthH = (byte)(byteWidth >> 8); + imageCommand.Append(new byte[] { Cmd.GS, Images.ImageCmdLegacy, 0x30, 0x00, widthL, widthH, heightL, heightH }); + } + else + { + byte widthL = (byte)width; + byte widthH = (byte)(width >> 8); + imageCommand.Append(new byte[] { 0x30, 0x70, 0x30, 0x01, 0x01, colorByte, widthL, widthH, heightL, heightH }); + } + + imageCommand.Append(imageData); + + // Load image to print buffer + ByteArrayBuilder response = new ByteArrayBuilder(); + byte[] imageCommandBytes = imageCommand.ToArray(); + if (!isLegacy) + { + response.Append(GetImageHeader(imageCommandBytes.Length)); + } + + response.Append(imageCommandBytes); + return response.ToArray(); + } + + public virtual byte[] WriteImageFromBuffer() + { + // Print image that's already buffered. + ByteArrayBuilder response = new ByteArrayBuilder(); + byte[] printCommandBytes = new byte[] { 0x30, 0x32 }; + response.Append(GetImageHeader(printCommandBytes.Length)); + response.Append(printCommandBytes); + return response.ToArray(); + } + + public virtual byte[] PrintImage(byte[] image, bool isHiDPI, bool isLegacy = false, int maxWidth = -1, int color = 1) + { + if (isLegacy) + { + return ByteSplicer.Combine(BufferImage(image, maxWidth, isLegacy)); + } + else + { + return ByteSplicer.Combine(SetImageDensity(isHiDPI), BufferImage(image, maxWidth, isLegacy, color), WriteImageFromBuffer()); + } + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/LineSpacingCommands.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/LineSpacingCommands.cs new file mode 100644 index 0000000..69e4f70 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/LineSpacingCommands.cs @@ -0,0 +1,11 @@ +using ESCPOS_NET.Emitters.BaseCommandValues; + +namespace ESCPOS_NET.Emitters +{ + public abstract partial class BaseCommandEmitter : ICommandEmitter + { + public virtual byte[] ResetLineSpacing() => new byte[] { Cmd.ESC, Whitespace.ResetLineSpacing }; + + public virtual byte[] SetLineSpacingInDots(int dots) => new byte[] { Cmd.ESC, Whitespace.LineSpacingInDots, (byte)dots }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/OperationalCommands.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/OperationalCommands.cs new file mode 100644 index 0000000..8f40497 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/OperationalCommands.cs @@ -0,0 +1,14 @@ +using ESCPOS_NET.Emitters.BaseCommandValues; + +namespace ESCPOS_NET.Emitters +{ + public abstract partial class BaseCommandEmitter : ICommandEmitter + { + /* Operational Commands */ + public virtual byte[] Initialize() => new byte[] { Cmd.ESC, Ops.Initialize }; + + public virtual byte[] Enable() => new byte[] { Cmd.ESC, Ops.EnableDisable, 1 }; + + public virtual byte[] Disable() => new byte[] { Cmd.ESC, Ops.EnableDisable, 0 }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/PrintCommands.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/PrintCommands.cs new file mode 100644 index 0000000..725a300 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/PrintCommands.cs @@ -0,0 +1,39 @@ +using System.Linq; +using ESCPOS_NET.Emitters.BaseCommandValues; + +namespace ESCPOS_NET.Emitters +{ + public abstract partial class BaseCommandEmitter : ICommandEmitter + { + /* Printing Commands */ + public virtual byte[] Print(string data) + { + // Fix OSX or Windows-style newlines + data = data.Replace("\r\n", "\n"); + data = data.Replace("\r", "\n"); + + // TODO: Sanitize... + return data.ToCharArray().Select(x => (byte)x).ToArray(); + } + + public virtual byte[] PrintLine(string line) + { + if (line == null) + { + return Print("\n"); + } + + return Print(line.Replace("\r", string.Empty).Replace("\n", string.Empty) + "\n"); + } + + public virtual byte[] FeedLines(int lineCount) => new byte[] { Cmd.ESC, Whitespace.FeedLines, (byte)lineCount }; + + public virtual byte[] FeedLinesReverse(int lineCount) => new byte[] { Cmd.ESC, Whitespace.FeedLinesReverse, (byte)lineCount }; + + public virtual byte[] FeedDots(int dotCount) => new byte[] { Cmd.ESC, Whitespace.FeedDots, (byte)dotCount }; + + public virtual byte[] ReverseMode(bool enable) => new byte[] { Cmd.GS, Chars.ReversePrintMode, enable ? (byte)0x01 : (byte)0x00 }; + + public virtual byte[] UpsideDownMode(bool enable) => new byte[] { Cmd.ESC, Chars.UpsideDownMode, enable ? (byte)0x01 : (byte)0x00 }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/StatusCommands.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/StatusCommands.cs new file mode 100644 index 0000000..ebe801f --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandEmitter/StatusCommands.cs @@ -0,0 +1,18 @@ +using ESCPOS_NET.Emitters.BaseCommandValues; + +namespace ESCPOS_NET.Emitters +{ + public abstract partial class BaseCommandEmitter : ICommandEmitter + { + /* Status Commands */ + public virtual byte[] EnableAutomaticStatusBack() => new byte[] { Cmd.GS, Status.AutomaticStatusBack, 0xFF }; + + public virtual byte[] EnableAutomaticInkStatusBack() => new byte[] { Cmd.GS, Status.AutomaticInkStatusBack, 0xFF }; + public virtual byte[] DisableAutomaticStatusBack() => new byte[] { Cmd.GS, Status.AutomaticStatusBack, 0x00 }; + + public virtual byte[] DisableAutomaticInkStatusBack() => new byte[] { Cmd.GS, Status.AutomaticInkStatusBack, 0x00 }; + public virtual byte[] RequestPaperStatus() => new byte[] { Cmd.GS, Status.RequestStatus, 0x31 }; + public virtual byte[] RequestDrawerStatus() => new byte[] { Cmd.GS, Status.RequestStatus, 0x32 }; + public virtual byte[] RequestInkStatus() => new byte[] { Cmd.GS, Status.RequestStatus, 0x34 }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Barcodes.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Barcodes.cs new file mode 100644 index 0000000..3b1fd80 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Barcodes.cs @@ -0,0 +1,26 @@ +namespace ESCPOS_NET.Emitters.BaseCommandValues +{ + public static class Barcodes + { + public static readonly byte PrintBarcode = 0x6B; + public static readonly byte SetBarcodeHeightInDots = 0x68; + public static readonly byte SetBarWidth = 0x77; + public static readonly byte SetBarLabelPosition = 0x48; + public static readonly byte SetBarLabelFont = 0x66; + + public static readonly byte Set2DCode = 0x28; + public static readonly byte AutoEnding = 0x00; + public static readonly byte[] SetPDF417NumberOfColumns = { 0x03, 0x00, 0x30, 0x41 }; + public static readonly byte[] SetPDF417NumberOfRows = { 0x03, 0x00, 0x30, 0x42 }; + public static readonly byte[] SetPDF417DotSize = { 0x03, 0x00, 0x30, 0x43 }; + public static readonly byte[] SetPDF417CorrectionLevel = { 0x04, 0x00, 0x30, 0x45, 0x30 }; + public static readonly byte[] StorePDF417Data = { 0x00, 0x30, 0x50, 0x30 }; + public static readonly byte[] PrintPDF417 = { 0x03, 0x00, 0x30, 0x51, 0x30 }; + + public static readonly byte[] SelectQRCodeModel = { 0x04, 0x00, 0x31, 0x41 }; + public static readonly byte[] SetQRCodeDotSize = { 0x03, 0x00, 0x31, 0x43 }; + public static readonly byte[] SetQRCodeCorrectionLevel = { 0x03, 0x00, 0x31, 0x45 }; + public static readonly byte[] StoreQRCodeData = { 0x31, 0x50, 0x30 }; + public static readonly byte[] PrintQRCode = { 0x03, 0x00, 0x31, 0x51, 0x30 }; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Chars.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Chars.cs new file mode 100644 index 0000000..c629b3a --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Chars.cs @@ -0,0 +1,13 @@ +namespace ESCPOS_NET.Emitters.BaseCommandValues +{ + public static class Chars + { + public static readonly byte StyleMode = 0x21; + public static readonly byte Alignment = 0x61; + public static readonly byte ReversePrintMode = 0x42; + public static readonly byte RightCharacterSpacing = 0x20; + public static readonly byte UpsideDownMode = 0x7B; + public static readonly byte CodePage = 0x74; + public static readonly byte Color = 0x72; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Cmd.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Cmd.cs new file mode 100644 index 0000000..40b4662 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Cmd.cs @@ -0,0 +1,8 @@ +namespace ESCPOS_NET.Emitters.BaseCommandValues +{ + public static class Cmd + { + public static readonly byte ESC = 0x1B; + public static readonly byte GS = 0x1D; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Display.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Display.cs new file mode 100644 index 0000000..e14d0f0 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Display.cs @@ -0,0 +1,7 @@ +namespace ESCPOS_NET.Emitters.BaseCommandValues +{ + public static class Display + { + public static readonly byte CLR = 0x0C; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Functions.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Functions.cs new file mode 100644 index 0000000..fbd9856 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Functions.cs @@ -0,0 +1,10 @@ +namespace ESCPOS_NET.Emitters.BaseCommandValues +{ + public static class Functions + { + public static readonly byte PaperCutFullCut = 0x00; + public static readonly byte PaperCutFullCutWithFeed = 0x41; + public static readonly byte PaperCutPartialCut = 0x01; + public static readonly byte PaperCutPartialCutWithFeed = 0x42; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Images.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Images.cs new file mode 100644 index 0000000..7f4cb99 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Images.cs @@ -0,0 +1,10 @@ +namespace ESCPOS_NET.Emitters.BaseCommandValues +{ + public static class Images + { + public static readonly byte ImageCmdParen = 0x28; + public static readonly byte ImageCmdLegacy = 0x76; + public static readonly byte ImageCmd8 = 0x38; + public static readonly byte ImageCmdL = 0x4C; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Ops.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Ops.cs new file mode 100644 index 0000000..9cf4263 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Ops.cs @@ -0,0 +1,10 @@ +namespace ESCPOS_NET.Emitters.BaseCommandValues +{ + public static class Ops + { + public static readonly byte Initialize = 0x40; + public static readonly byte EnableDisable = 0x3D; + public static readonly byte PaperCut = 0x56; + public static readonly byte CashDrawerPulse = 0x70; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Status.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Status.cs new file mode 100644 index 0000000..cc819e6 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Status.cs @@ -0,0 +1,9 @@ +namespace ESCPOS_NET.Emitters.BaseCommandValues +{ + public static class Status + { + public static readonly byte AutomaticStatusBack = 0x61; + public static readonly byte AutomaticInkStatusBack = 0x6A; + public static readonly byte RequestStatus = 0x72; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Whitespace.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Whitespace.cs new file mode 100644 index 0000000..f679a76 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/BaseCommandValues/Whitespace.cs @@ -0,0 +1,13 @@ +namespace ESCPOS_NET.Emitters.BaseCommandValues +{ + public static class Whitespace + { + // TODO: tabs? + public static readonly byte Linefeed = 0x0A; + public static readonly byte FeedLines = 0x64; + public static readonly byte FeedLinesReverse = 0x65; + public static readonly byte FeedDots = 0x4A; + public static readonly byte ResetLineSpacing = 0x32; + public static readonly byte LineSpacingInDots = 0x33; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/EPSON.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/EPSON.cs new file mode 100644 index 0000000..8648b63 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/EPSON.cs @@ -0,0 +1,6 @@ +namespace ESCPOS_NET.Emitters +{ + public class EPSON : BaseCommandEmitter + { + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/2DCode.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/2DCode.cs new file mode 100644 index 0000000..f1616c8 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/2DCode.cs @@ -0,0 +1,30 @@ +namespace ESCPOS_NET.Emitters +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "Enums are easier to read if they have whitespace alignment.")] + public enum TwoDimensionCodeType + { + PDF417 = 0, + QRCODE_MODEL1 = 49, + QRCODE_MODEL2 = 50, + QRCODE_MICRO = 51, + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "Enums are easier to read if they have whitespace alignment.")] + public enum Size2DCode + { + TINY = 2, + SMALL = 3, + NORMAL = 4, + LARGE = 5, + EXTRA = 6, + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "Enums are easier to read if they have whitespace alignment.")] + public enum CorrectionLevel2DCode + { + PERCENT_7 = 48, + PERCENT_15 = 49, + PERCENT_25 = 50, + PERCENT_30 = 51, + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/Align.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/Align.cs new file mode 100644 index 0000000..be7ef6b --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/Align.cs @@ -0,0 +1,10 @@ +namespace ESCPOS_NET.Emitters +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "Enums are easier to read if they have whitespace alignment.")] + public enum Align + { + Left = 0, + Center = 1, + Right = 2, + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/BarLabelPrintPosition.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/BarLabelPrintPosition.cs new file mode 100644 index 0000000..9c9d35e --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/BarLabelPrintPosition.cs @@ -0,0 +1,11 @@ +namespace ESCPOS_NET.Emitters +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "Enums are easier to read if they have whitespace alignment..")] + public enum BarLabelPrintPosition + { + None = 0, + Above = 1, + Below = 2, + Both = 3, + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/BarWidth.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/BarWidth.cs new file mode 100644 index 0000000..2023ec4 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/BarWidth.cs @@ -0,0 +1,12 @@ +namespace ESCPOS_NET.Emitters +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "Enums are easier to read if they have whitespace alignment.")] + public enum BarWidth + { + Thinnest = 2, + Thin = 3, + Default = 4, + Thick = 5, + Thickest = 6, + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/BarcodeCode.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/BarcodeCode.cs new file mode 100644 index 0000000..e515640 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/BarcodeCode.cs @@ -0,0 +1,9 @@ +namespace ESCPOS_NET.Emitters +{ + public enum BarcodeCode + { + CODE_A = 0x41, + CODE_B = 0x42, + CODE_C = 0x43, + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/BarcodeType.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/BarcodeType.cs new file mode 100644 index 0000000..3e7a5d7 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/BarcodeType.cs @@ -0,0 +1,21 @@ +namespace ESCPOS_NET.Emitters +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "Enums are easier to read if they have whitespace alignment.")] + public enum BarcodeType // All from Function B + { + UPC_A = 0x41, + UPC_E = 0x42, + JAN13_EAN13 = 0x43, + JAN8_EAN8 = 0x44, + CODE39 = 0x45, + ITF = 0x46, + CODABAR_NW_7 = 0x47, + CODE93 = 0x48, + CODE128 = 0x49, + GS1_128 = 0x4A, + GS1_DATABAR_OMNIDIRECTIONAL = 0x4B, + GS1_DATABAR_TRUNCATED = 0x4C, + GS1_DATABAR_LIMITED = 0x4D, + GS1_DATABAR_EXPANDED = 0x4E, + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/CodePage.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/CodePage.cs new file mode 100644 index 0000000..83e9eb1 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/CodePage.cs @@ -0,0 +1,68 @@ +namespace ESCPOS_NET.Emitters +{ + #pragma warning disable SA1602 // Enumeration items should be documented + #pragma warning disable SA1600 // Elements should be documented + public enum CodePage + { + PC437_USA_STANDARD_EUROPE_DEFAULT = 0, + KATAKANA = 1, + PC850_MULTILINGUAL = 2, + PC860_PORTUGUESE = 3, + PC863_CANADIAN_FRENCH = 4, + PC865_NORDIC = 5, + HIRAGANA = 6, + ONE_PASS_KANJI = 7, + ONE_PASS_KANJI2 = 8, + PC851_GREEK = 11, + PC853_TURKISH = 12, + PC857_TURKISH = 13, + PC737_GREEK = 14, + ISO8859_7_GREEK = 15, + WPC1252 = 16, + PC866_CYRILLIC2 = 17, + PC852_LATIN2 = 18, + PC858_EURO = 19, + KU42_THAI = 20, + TIS11_THAI = 21, + TIS13_THAI = 22, + TIS14_THAI = 23, + TIS16_THAI = 24, + TIS17_THAI = 25, + TIS18_THAI = 26, + TCVN3_VIETNAMESE_L = 30, + TCVN3_VIETNAMESE_U = 31, + PC720_ARABIC = 32, + WPC775_BALTIC_RIM = 33, + PC855_CYRILLIC = 34, + PC861_ICELANDIC = 35, + PC862_HEBREW = 36, + PC864_ARABIC = 37, + PC869_GREEK = 38, + ISO8859_2_LATIN2 = 39, + ISO8859_15_LATIN9 = 40, + PC1098_FARSI = 41, + PC1118_LITHUANIAN = 42, + PC1119_LITHUANIAN = 43, + PC1125_UKRANIAN = 44, + WPC1250_LATIN2 = 45, + WPC1251_CYRILLIC = 46, + WPC1253_GREEK = 47, + WPC1254_TURKISH = 48, + WPC1255_HEBREW = 49, + WPC1256_ARABIC = 50, + WPC1257_BALTIC_RIM = 51, + WPC1258_VIETNAMESE = 52, + KZ1048_KAZAKHSTAN = 53, + DEVANAGARI = 66, + BENGALI = 67, + TAMIL = 68, + TELUGU = 69, + ASSAMESE = 70, + ORIYA = 71, + KANNADA = 72, + MALAYALAM = 73, + GUJARATI = 74, + PUNJABI = 75, + MARATHI = 82, + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/Color.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/Color.cs new file mode 100644 index 0000000..ed0055b --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/Color.cs @@ -0,0 +1,12 @@ +using System; + +namespace ESCPOS_NET.Emitters +{ + [Flags] + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "Enums are easier to read if they have whitespace alignment.")] + public enum Color + { + Black = 0, + Red = 1 + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/PrintStyle.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/PrintStyle.cs new file mode 100644 index 0000000..ab2b921 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/Enums/PrintStyle.cs @@ -0,0 +1,16 @@ +using System; + +namespace ESCPOS_NET.Emitters +{ + [Flags] + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "Enums are easier to read if they have whitespace alignment.")] + public enum PrintStyle + { + None = 0, + FontB = 1, + Bold = 1 << 3, + DoubleHeight = 1 << 4, + DoubleWidth = 1 << 5, + Underline = 1 << 7, + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/ICommandEmitter.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/ICommandEmitter.cs new file mode 100644 index 0000000..2641326 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Emitters/ICommandEmitter.cs @@ -0,0 +1,92 @@ +namespace ESCPOS_NET.Emitters +{ + public interface ICommandEmitter + { + /* Print Commands */ + byte[] PrintLine(string line = null); + + byte[] Print(string line); + + byte[] FeedLines(int lineCount); + + byte[] FeedLinesReverse(int lineCount); + + byte[] FeedDots(int dotCount); + + /* Line Spacing Commands */ + byte[] ResetLineSpacing(); + + byte[] SetLineSpacingInDots(int dots); + + /* Operational Commands */ + byte[] Initialize(); + + byte[] Enable(); + + byte[] Disable(); + + /* Cash Drawer Commands */ + byte[] CashDrawerOpenPin2(int pulseOnTimeMs = 120, int pulseOffTimeMs = 240); + + byte[] CashDrawerOpenPin5(int pulseOnTimeMs = 120, int pulseOffTimeMs = 240); + + /* Character Commands */ + byte[] SetStyles(PrintStyle style); + + byte[] LeftAlign(); + + byte[] RightAlign(); + + byte[] CenterAlign(); + + byte[] ReverseMode(bool activate); + + byte[] RightCharacterSpacing(int spaceCount); + + byte[] UpsideDownMode(bool activate); + + byte[] CodePage(CodePage codePage); + + byte[] Color(Color color); + + /* Action Commands */ + byte[] FullCut(); + + byte[] PartialCut(); + + byte[] FullCutAfterFeed(int lineCount); + + byte[] PartialCutAfterFeed(int lineCount); + + /* Image Commands */ + byte[] SetImageDensity(bool isHiDPI); + + byte[] BufferImage(byte[] image, int maxWidth, bool isLegacy = false, int color = 1); + + byte[] WriteImageFromBuffer(); + + byte[] PrintImage(byte[] image, bool isHiDPI, bool isLegacy = false, int maxWidth = -1, int color = 1); + + /* Status Commands */ + byte[] EnableAutomaticStatusBack(); + + byte[] EnableAutomaticInkStatusBack(); + + /* Barcode Commands */ + byte[] PrintBarcode(BarcodeType type, string barcode, BarcodeCode code = BarcodeCode.CODE_B); + + byte[] PrintQRCode(string data, TwoDimensionCodeType type = TwoDimensionCodeType.QRCODE_MODEL2, Size2DCode size = Size2DCode.NORMAL, CorrectionLevel2DCode correction = CorrectionLevel2DCode.PERCENT_7); + + byte[] Print2DCode(TwoDimensionCodeType type, string data, Size2DCode size = Size2DCode.NORMAL, CorrectionLevel2DCode correction = CorrectionLevel2DCode.PERCENT_7); + + byte[] SetBarcodeHeightInDots(int height); + + byte[] SetBarWidth(BarWidth width); + + byte[] SetBarLabelPosition(BarLabelPrintPosition position); + + byte[] SetBarLabelFontB(bool fontB); + + /* 2D-Code Commands */ + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Extensions/ImageSharpExtensions.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Extensions/ImageSharpExtensions.cs new file mode 100644 index 0000000..a21cea9 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Extensions/ImageSharpExtensions.cs @@ -0,0 +1,64 @@ +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +namespace SixLabors.ImageSharp +{ + public static class ImageSharpExtensions + { + public static byte[] ToSingleBitPixelByteArray(this Image image, bool rasterFormat = true, int? maxWidth = null, int? maxHeight = null, float threshold = 0.5F) + { + image.Mutate(img => + { + if (maxWidth.HasValue || maxHeight.HasValue) + { + img.Resize(new ResizeOptions + { + Mode = ResizeMode.Max, + Size = new Size(maxWidth ?? int.MaxValue, maxHeight ?? int.MaxValue), + }); + } + + img.BackgroundColor(Color.White); + img.Grayscale().BinaryDither(KnownDitherings.Stucki); + + if (!rasterFormat) + { + img.Rotate(RotateMode.Rotate90); + img.Flip(FlipMode.Horizontal); + } + }); + + var result = image.ToSingleBitPixelByteArray(); + return result; + } + + private static byte[] ToSingleBitPixelByteArray(this Image image) + { + var bytesPerRow = (image.Width + 7 & -8) / 8; + + var result = new byte[bytesPerRow * image.Height]; + for (int y = 0; y < image.Height; y++) + { + var row = image.GetPixelRowSpan(y); + var rowStartPosition = y * bytesPerRow; + + for (int x = 0; x < row.Length; x++) + { + if (!row[x].IsBlack()) + { + continue; + } + + result[rowStartPosition + (x / 8)] |= (byte)(0x01 << (7 - (x % 8))); + } + } + + return result; + } + + private static bool IsBlack(this Rgba32 rgba) + { + return rgba.R == 0 && rgba.G == 0 && rgba.B == 0; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Extensions/TaskExtensions.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Extensions/TaskExtensions.cs new file mode 100644 index 0000000..230af68 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Extensions/TaskExtensions.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace ESCPOS_NET.Extensions +{ + public static class TaskExtensions + { + public static void LogExceptions(this Task task) + { + task.ContinueWith(t => + { + var aggException = t.Exception.Flatten(); + foreach (var exception in aggException.InnerExceptions) + { + Logging.Logger?.LogError(exception, "Uncaught Thread Exception."); + } + }, + TaskContinuationOptions.OnlyOnFaulted); + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Extensions/Util.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Extensions/Util.cs new file mode 100644 index 0000000..c1d7f1c --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Extensions/Util.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; + +namespace ESCPOS_NET.Extensions +{ + public static class Util + { + /// + /// Adds to the list of T items and/or enumerables of T. All other types will be ignored and not added to the list. + /// + /// List's items type. + /// List to be added the items. + /// Items to be added. + /// True if no item was ignored, otherwise False. + public static bool AddRange(this List list, params object[] items) + { + bool ignoredItems = false; + foreach (var item in items) + { + if (item is T itemT) + { + list.Add(itemT); + } + else if (item is IEnumerable arrayT) + { + list.AddRange(arrayT); + } + else + { + ignoredItems = true; + } + } + + return !ignoredItems; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/GlobalSuppressions.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/GlobalSuppressions.cs new file mode 100644 index 0000000..b404a03 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/GlobalSuppressions.cs @@ -0,0 +1,9 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1200:Using directives should be placed correctly", Justification = "Placing usings within namespaces is not default in VS or in generated code.")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1101:Prefix local calls with this", Justification = "Visual Studio by default recommends removing prefixed 'this'.")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1309:Field names should not begin with underscore", Justification = "MS style guide uses _ for private members.")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "", Scope = "type", Target = "~T:ESCPOS_NET.Emitters.CodePage")] diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/LICENSE b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/LICENSE new file mode 100644 index 0000000..5631092 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Luke Paireepinart + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Logging/BaseLogger.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Logging/BaseLogger.cs new file mode 100644 index 0000000..a7ce46c --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Logging/BaseLogger.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; + +namespace ESCPOS_NET +{ + public static class Logging + { + public static ILogger Logger { get; set; } + static Logging() + { + + TaskScheduler.UnobservedTaskException += (s, e) => + { + //LogUnhandledException(e.Exception, "TaskScheduler.UnobservedTaskException"); + e.SetObserved(); + }; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/BasePrinter.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/BasePrinter.cs new file mode 100644 index 0000000..c5d3386 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/BasePrinter.cs @@ -0,0 +1,378 @@ +using ESCPOS_NET.Emitters.BaseCommandValues; +using ESCPOS_NET.Utilities; +using ESCPOS_NET.Utils; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using System.Timers; + +namespace ESCPOS_NET +{ + public abstract partial class BasePrinter : IPrinter, IDisposable + { + private bool disposed = false; + + //private volatile bool _isMonitoring; + + private CancellationTokenSource _readCancellationTokenSource; + private CancellationTokenSource _writeCancellationTokenSource; + + private readonly int _maxBytesPerWrite = 15000; // max byte chunks to write at once. + + public PrinterStatusEventArgs Status { get; private set; } = new PrinterStatusEventArgs(); + + public event EventHandler StatusChanged; + public event EventHandler Disconnected; + public event EventHandler Connected; + //public event EventHandler WriteFailed; + //public event EventHandler Idle; + + protected BinaryWriter Writer { get; set; } + + protected BinaryReader Reader { get; set; } + + protected ConcurrentQueue ReadBuffer { get; set; } = new ConcurrentQueue(); + + protected ConcurrentQueue WriteBuffer { get; set; } = new ConcurrentQueue(); + + protected int BytesWrittenSinceLastFlush { get; set; } = 0; + + protected volatile bool IsConnected = true; + + public string PrinterName { get; protected set; } + + protected BasePrinter() + { + PrinterName = Guid.NewGuid().ToString(); + Init(); + } + protected BasePrinter(string printerName) + { + if (string.IsNullOrEmpty(printerName)) + { + printerName = Guid.NewGuid().ToString(); + } + PrinterName = printerName; + Init(); + } + private void Init() + { + _readCancellationTokenSource = new CancellationTokenSource(); + _writeCancellationTokenSource = new CancellationTokenSource(); + Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Initializing Task Threads...", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + //Task.Factory.StartNew(MonitorPrinterStatusLongRunningTask, _connectivityCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false); + Task.Factory.StartNew(WriteLongRunningTask, _writeCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false); + Task.Factory.StartNew(ReadLongRunningTask, _readCancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false); + // TODO: read and status monitoring probably won't work for fileprinter, should let printer types disable this feature. + Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Task Threads started", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + } + + + protected void InvokeConnect() + { + Task.Run(() => Connected?.Invoke(this, new ConnectionEventArgs() { IsConnected = true })); + } + protected void InvokeDisconnect() + { + Task.Run(() => Disconnected?.Invoke(this, new ConnectionEventArgs() { IsConnected = false })); + } + + protected virtual void Reconnect() + { + // Implemented in the network printer + } + protected virtual async void WriteLongRunningTask() + { + while (true) + { + if (_writeCancellationTokenSource != null && _writeCancellationTokenSource.IsCancellationRequested) + { + Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Write Long-Running Task Cancellation was requested.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + break; + } + + await Task.Delay(100); + if (!IsConnected) + { + continue; + } + + try + { + var didDequeue = WriteBuffer.TryDequeue(out var nextBytes); + if (didDequeue && nextBytes?.Length > 0) + { + WriteToBinaryWriter(nextBytes); + } + } + catch (IOException ex) + { + // Thrown if the printer times out the socket connection + // default is 90 seconds + //Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Swallowing IOException... sometimes happens with network printers. Should get reconnected automatically."); + } + catch (Exception ex) + { + // Swallow the exception + //Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Swallowing generic read exception... sometimes happens with serial port printers."); + } + } + } + + protected virtual async void ReadLongRunningTask() + { + while (true) + { + if (_readCancellationTokenSource != null && _readCancellationTokenSource.IsCancellationRequested) + { + Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Read Long-Running Task Cancellation was requested.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + break; + } + + await Task.Delay(100); + + if (Reader == null) continue; + if (!IsConnected) continue; + + try + { + // Sometimes the serial port lib will throw an exception and read past the end of the queue if a + // status changes while data is being written. We just ignore these bytes. + var b = Reader.BaseStream.ReadByte(); + if (b >= 0 && b <= 255) + { + ReadBuffer.Enqueue((byte)b); + DataAvailable(); + } + } + + catch (Exception ex) + { + // Swallow the exception + //Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Swallowing generic read exception... sometimes happens with serial port printers.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + } + } + } + + public virtual void Write(params byte[][] arrays) + { + Write(ByteSplicer.Combine(arrays)); + } + + public virtual void Write(byte[] bytes) + { + WriteBuffer.Enqueue(bytes); + } + + protected virtual void WriteToBinaryWriter(byte[] bytes) + { + + if (!IsConnected) + { + Logging.Logger?.LogInformation("[{Function}]:[{PrinterName}] Attempted to write but printer isn't connected. Attempting to reconnect...", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + Reconnect(); + } + + if (!IsConnected) + { + Logging.Logger?.LogError("[{Function}]:[{PrinterName}] Unrecoverable connectivity error writing to printer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + throw new IOException("Unrecoverable connectivity error writing to printer."); + } + + int bytePointer = 0; + int bytesLeft = bytes.Length; + bool hasFlushed = false; + while (bytesLeft > 0) + { + + int count = Math.Min(_maxBytesPerWrite, bytesLeft); + try + { + Writer.Write(bytes, bytePointer, count); + } + catch (IOException e) + { + Reconnect(); + if (!IsConnected) + { + Logging.Logger?.LogError(e, "[{Function}]:[{PrinterName}] Unrecoverable connectivity error writing to printer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + } + Writer.Write(bytes, bytePointer, count); + } + BytesWrittenSinceLastFlush += count; + if (BytesWrittenSinceLastFlush >= 200) + { + // Immediately trigger a flush before proceeding so the output buffer will not be delayed. + hasFlushed = true; + Flush(null, null); + } + + bytePointer += count; + bytesLeft -= count; + } + + if (!hasFlushed) + { + Task.Run(async () => { await Task.Delay(50); Flush(null, null); }); + } + } + + protected virtual void Flush(object sender, ElapsedEventArgs e) + { + try + { + + BytesWrittenSinceLastFlush = 0; + Writer.Flush(); + } + catch (Exception ex) + { + Logging.Logger?.LogError(ex, "[{Function}]:[{PrinterName}] Flush threw exception.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + } + } + + public virtual void DataAvailable() + { + if (ReadBuffer.Count() % 4 == 0) + { + var bytes = new byte[4]; + for (int i = 0; i < 4; i++) + { + if (!ReadBuffer.TryDequeue(out bytes[i])) + { + // Ran out of bytes unexpectedly. + return; + } + } + + TryUpdatePrinterStatus(bytes); + + // TODO: call other update handlers. + } + } + + private void TryUpdatePrinterStatus(byte[] bytes) + { + var bytesToString = BitConverter.ToString(bytes); + + Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] TryUpdatePrinterStatus: Received flag values {bytesToString}", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName, bytesToString); + + // Check header bits 0, 1 and 7 are 0, and 4 is 1 + if (bytes[0].IsBitNotSet(0) && bytes[0].IsBitNotSet(1) && bytes[0].IsBitSet(4) && bytes[0].IsBitNotSet(7)) + { + Status = new PrinterStatusEventArgs() + { + // byte[0] == 20 cash drawer closed + // byte[0] == 16 cash drawer open + // Note some cash drawers do not close properly. + IsCashDrawerOpen = bytes[0].IsBitNotSet(2), + IsPrinterOnline = bytes[0].IsBitNotSet(3), + IsCoverOpen = bytes[0].IsBitSet(5), + IsPaperCurrentlyFeeding = bytes[0].IsBitSet(6), + IsWaitingForOnlineRecovery = bytes[1].IsBitSet(0), + IsPaperFeedButtonPushed = bytes[1].IsBitSet(1), + DidRecoverableNonAutocutterErrorOccur = bytes[1].IsBitSet(2), + DidAutocutterErrorOccur = bytes[1].IsBitSet(3), + DidUnrecoverableErrorOccur = bytes[1].IsBitSet(5), + DidRecoverableErrorOccur = bytes[1].IsBitSet(6), + IsPaperLow = bytes[2].IsBitSet(0) && bytes[2].IsBitSet(1), + IsPaperOut = bytes[2].IsBitSet(2) && bytes[2].IsBitSet(3), + }; + } + + if (StatusChanged != null) + { + Logging.Logger?.LogDebug("[{Function}]:[{PrinterName}] Invoking Status Changed Event Handler...", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + StatusChanged?.Invoke(this, Status); + } + } + + ~BasePrinter() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void OverridableDispose() // This method should only be called by the Dispose method. // It allows synchronous disposing of derived class dependencies with base class disposes. + { + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + { + return; + } + + if (disposing) + { + try + { + _readCancellationTokenSource?.Cancel(); + } + catch (Exception e) + { + Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue during cancellation token cancellation call.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + } + try + { + Reader?.Close(); + } + catch (Exception e) + { + Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue closing reader.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + } + try + { + Reader?.Dispose(); + } + catch (Exception e) + { + Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue disposing reader.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + } + try + { + Writer?.Close(); + } + catch (Exception e) + { + Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue closing writer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + } + try + { + Writer?.Dispose(); + } + catch (Exception e) + { + Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue disposing writer.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + } + try + { + OverridableDispose(); + } + catch (Exception e) + { + Logging.Logger?.LogDebug(e, "[{Function}]:[{PrinterName}] Dispose Issue during overridable dispose.", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName); + } + } + + disposed = true; + } + + public PrinterStatusEventArgs GetStatus() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/ConnectionEventArgs.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/ConnectionEventArgs.cs new file mode 100644 index 0000000..3cfde19 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/ConnectionEventArgs.cs @@ -0,0 +1,9 @@ +using System; + +namespace ESCPOS_NET +{ + public class ConnectionEventArgs : EventArgs + { + public bool IsConnected; + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/FilePrinter.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/FilePrinter.cs new file mode 100644 index 0000000..d49ad23 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/FilePrinter.cs @@ -0,0 +1,36 @@ +using System.IO; + +namespace ESCPOS_NET +{ + public class FilePrinter : BasePrinter + { + private readonly FileStream _file; + + // TODO: default values to their default values in ctor. + public FilePrinter(string filePath, bool createIfNotExists = false) + : base() + { + if (createIfNotExists) + { + _file = File.Open(filePath, FileMode.OpenOrCreate); + } + else + { + _file = File.Open(filePath, FileMode.Open); + } + Writer = new BinaryWriter(_file); + Reader = new BinaryReader(_file); + } + + ~FilePrinter() + { + Dispose(false); + } + + protected override void OverridableDispose() + { + _file?.Close(); + _file?.Dispose(); + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/IPrinter.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/IPrinter.cs new file mode 100644 index 0000000..e51805d --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/IPrinter.cs @@ -0,0 +1,17 @@ +using System; + +namespace ESCPOS_NET +{ + public interface IPrinter + { + PrinterStatusEventArgs GetStatus(); + void Write(params byte[][] arrays); + void Write(byte[] bytes); + event EventHandler StatusChanged; + event EventHandler Disconnected; + event EventHandler Connected; + //event EventHandler WriteFailed; + //event EventHandler Idle; + //event EventHandler IdleDisconnected; is this useful? to know that it disconnected because of idle? probably better to have this as info in disconnected event object instead. + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/MemoryPrinter.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/MemoryPrinter.cs new file mode 100644 index 0000000..11060f1 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/MemoryPrinter.cs @@ -0,0 +1,33 @@ +using System.IO; + +namespace ESCPOS_NET +{ + public class MemoryPrinter : BasePrinter + { + private readonly MemoryStream _ms; + + // TODO: default values to their default values in ctor. + public MemoryPrinter() + : base() + { + _ms = new MemoryStream(); + Writer = new BinaryWriter(_ms); + } + + ~MemoryPrinter() + { + Dispose(false); + } + + public byte[] GetAllData() + { + return _ms.ToArray(); + } + + protected override void OverridableDispose() + { + _ms?.Close(); + _ms?.Dispose(); + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/NetworkPrinter.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/NetworkPrinter.cs new file mode 100644 index 0000000..4d9ca19 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/NetworkPrinter.cs @@ -0,0 +1,101 @@ +using Microsoft.Extensions.Logging; +using SimpleTcp; +using System; +using System.IO; +using System.Net; +using System.Text.Json; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Reflection; + +namespace ESCPOS_NET +{ + public class NetworkPrinterSettings + { + // Connection string is of the form printer_name:port or ip:port + public string ConnectionString { get; set; } + public EventHandler ConnectedHandler { get; set; } + public EventHandler DisconnectedHandler { get; set; } + //public bool ReconnectOnTimeout { get; set; } + //public uint? ReceiveTimeoutMs { get; set; } + //public uint? SendTimeoutMs { get; set; } + //public uint? ConnectTimeoutMs { get; set; } + //public uint? ReconnectTimeoutMs { get; set; } + //public uint? MaxReconnectAttempts { get; set; } + public string PrinterName { get; set; } + } + public class NetworkPrinter : BasePrinter + { + private readonly NetworkPrinterSettings _settings; + private TCPConnection _tcpConnection; + + public NetworkPrinter(NetworkPrinterSettings settings) : base(settings.PrinterName) + { + _settings = settings; + if (settings.ConnectedHandler != null) + { + Connected += settings.ConnectedHandler; + } + if (settings.DisconnectedHandler != null) + { + Disconnected += settings.DisconnectedHandler; + } + Connect(); + } + + private void ConnectedEvent(object sender, ClientConnectedEventArgs e) + { + Logging.Logger?.LogInformation("[{Function}]:[{PrinterName}] Connected successfully to network printer! Connection String: {ConnectionString}", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName, _settings.ConnectionString); + IsConnected = true; + InvokeConnect(); + } + private void DisconnectedEvent(object sender, ClientDisconnectedEventArgs e) + { + IsConnected = false; + InvokeDisconnect(); + Logging.Logger?.LogWarning("[{Function}]:[{PrinterName}] Network printer connection terminated. Attempting to reconnect. Connection String: {ConnectionString}", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName, _settings.ConnectionString); + Connect(); + } + private void AttemptReconnectInfinitely() + { + try + { + //_tcpConnection.ConnectWithRetries(300000); + _tcpConnection.ConnectWithRetries(3000); + } + catch (Exception e) + { + //Logging.Logger?.LogWarning("[{Function}]:[{PrinterName}] Network printer unable to connect after 5 minutes. Attempting to reconnect. Settings: {Settings}", $"{this}.{MethodBase.GetCurrentMethod().Name}", PrinterName, JsonSerializer.Serialize(_settings)); + Task.Run(async () => { await Task.Delay(250); Connect(); }); + } + } + + private void Connect() + { + if (_tcpConnection != null) + { + _tcpConnection.Connected -= ConnectedEvent; + _tcpConnection.Disconnected -= DisconnectedEvent; + } + + // instantiate + _tcpConnection = new TCPConnection(_settings.ConnectionString); + + // set events + _tcpConnection.Connected += ConnectedEvent; + _tcpConnection.Disconnected += DisconnectedEvent; + + Reader = new BinaryReader(_tcpConnection.ReadStream); + Writer = new BinaryWriter(_tcpConnection.WriteStream); + + Task.Run(() => { AttemptReconnectInfinitely(); }); + } + + protected override void OverridableDispose() + { + _tcpConnection = null; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/PrinterOptions.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/PrinterOptions.cs new file mode 100644 index 0000000..dbac2be --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/PrinterOptions.cs @@ -0,0 +1,8 @@ +namespace ESCPOS_NET +{ + public class PrinterOptions + { + int IdleTimeoutSeconds { get; set; } = 60; + int StatusPollingIntervalMilliseconds { get; set; } = 500; + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/PrinterStatusEventArgs.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/PrinterStatusEventArgs.cs new file mode 100644 index 0000000..2e3148e --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/PrinterStatusEventArgs.cs @@ -0,0 +1,34 @@ +using System; + +namespace ESCPOS_NET +{ + public class PrinterStatusEventArgs : EventArgs + { + public bool? IsWaitingForOnlineRecovery { get; set; } + + public bool? IsPaperCurrentlyFeeding { get; set; } + + public bool? IsPaperFeedButtonPushed { get; set; } + + public bool? IsPrinterOnline { get; set; } + + public bool? IsCashDrawerOpen { get; set; } + + public bool? IsCoverOpen { get; set; } + + public bool? IsPaperLow { get; set; } + + public bool? IsPaperOut { get; set; } + + public bool? IsInErrorState => (DidRecoverableErrorOccur ?? false) || (DidUnrecoverableErrorOccur ?? false) || (DidAutocutterErrorOccur ?? false) || (DidRecoverableNonAutocutterErrorOccur ?? false); + + public bool? DidRecoverableErrorOccur { get; set; } + + public bool? DidUnrecoverableErrorOccur { get; set; } + + public bool? DidAutocutterErrorOccur { get; set; } + + public bool? DidRecoverableNonAutocutterErrorOccur { get; set; } + + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/SerialPrinter.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/SerialPrinter.cs new file mode 100644 index 0000000..a9a6041 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/SerialPrinter.cs @@ -0,0 +1,27 @@ +using System.IO; +using System.IO.Ports; +using System.Threading.Tasks; + +namespace ESCPOS_NET +{ + public class SerialPrinter : BasePrinter + { + private readonly SerialPort _serialPort; + + public SerialPrinter(string portName, int baudRate) + : base() + { + _serialPort = new SerialPort(portName, baudRate); + _serialPort.Open(); + Writer = new BinaryWriter(_serialPort.BaseStream); + Reader = new BinaryReader(_serialPort.BaseStream); + } + + protected override void OverridableDispose() + { + _serialPort?.Close(); + _serialPort?.Dispose(); + Task.Delay(250).Wait(); // Based on MSDN Documentation, should sleep after calling Close or some functionality will not be determinant. + } + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/VirtualPrinter.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/VirtualPrinter.cs new file mode 100644 index 0000000..c7e692e --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Printers/VirtualPrinter.cs @@ -0,0 +1,9 @@ +// TODO: Implement this class + +namespace ESCPOS_NET +{ + public class VirtualPrinter + { + public byte[] Output { get; private set; } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Utils/Consts.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Utils/Consts.cs new file mode 100644 index 0000000..595fa75 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Utils/Consts.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ESCPOS_NET.Utils +{ + public static class Consts + { + public static readonly string LIBNAME = "ESCPOS_NET"; + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Utils/EchoStream.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Utils/EchoStream.cs new file mode 100644 index 0000000..7013a12 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Utils/EchoStream.cs @@ -0,0 +1,185 @@ + +using System; +using System.IO; +using System.Threading.Tasks; +using System.Threading; +using System.Collections.Concurrent; + +namespace ESCPOS_NET.Utils +{ + + public class EchoStream : Stream + { + public override bool CanTimeout { get; } = true; + public override int ReadTimeout { get; set; } = Timeout.Infinite; + public override int WriteTimeout { get; set; } = Timeout.Infinite; + public override bool CanRead { get; } = true; + public override bool CanSeek { get; } = false; + public override bool CanWrite { get; } = true; + + public bool CopyBufferOnWrite { get; set; } = false; + + private readonly object _lock = new object(); + + // Default underlying mechanism for BlockingCollection is ConcurrentQueue, which is what we want + private readonly BlockingCollection _Buffers; + private int _maxQueueDepth = 10; + + private byte[] m_buffer = null; + private int m_offset = 0; + private int m_count = 0; + + private bool m_Closed = false; + public override void Close() + { + m_Closed = true; + + // release any waiting writes + _Buffers.CompleteAdding(); + } + + public bool DataAvailable + { + get + { + return _Buffers.Count > 0; + } + } + + private long _Length = 0L; + public override long Length + { + get + { + return _Length; + } + } + + private long _Position = 0L; + public override long Position + { + get + { + return _Position; + } + set + { + throw new NotImplementedException(); + } + } + + public EchoStream() : this(10) + { + } + + public EchoStream(int maxQueueDepth) + { + _maxQueueDepth = maxQueueDepth; + _Buffers = new BlockingCollection(_maxQueueDepth); + } + + // we override the xxxxAsync functions because the default base class shares state between ReadAsync and WriteAsync, which causes a hang if both are called at once + public new Task WriteAsync(byte[] buffer, int offset, int count) + { + return Task.Run(() => Write(buffer, offset, count)); + } + + // we override the xxxxAsync functions because the default base class shares state between ReadAsync and WriteAsync, which causes a hang if both are called at once + public new Task ReadAsync(byte[] buffer, int offset, int count) + { + return Task.Run(() => + { + return Read(buffer, offset, count); + }); + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (m_Closed || buffer.Length - offset < count || count <= 0) + return; + + byte[] newBuffer; + if (!CopyBufferOnWrite && offset == 0 && count == buffer.Length) + newBuffer = buffer; + else + { + newBuffer = new byte[count]; + System.Buffer.BlockCopy(buffer, offset, newBuffer, 0, count); + } + if (!_Buffers.TryAdd(newBuffer, WriteTimeout)) + throw new TimeoutException("EchoStream Write() Timeout"); + + _Length += count; + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0) + return 0; + lock (_lock) + { + if (m_count == 0 && _Buffers.Count == 0) + { + if (m_Closed) + return -1; + + if (_Buffers.TryTake(out m_buffer, ReadTimeout)) + { + m_offset = 0; + m_count = m_buffer.Length; + } + else + return m_Closed ? -1 : 0; + } + + int returnBytes = 0; + while (count > 0) + { + if (m_count == 0) + { + if (_Buffers.TryTake(out m_buffer, 0)) + { + m_offset = 0; + m_count = m_buffer.Length; + } + else + break; + } + + var bytesToCopy = (count < m_count) ? count : m_count; + System.Buffer.BlockCopy(m_buffer, m_offset, buffer, offset, bytesToCopy); + m_offset += bytesToCopy; + m_count -= bytesToCopy; + offset += bytesToCopy; + count -= bytesToCopy; + + returnBytes += bytesToCopy; + } + + _Position += returnBytes; + + return returnBytes; + } + } + + public override int ReadByte() + { + byte[] returnValue = new byte[1]; + return (Read(returnValue, 0, 1) <= 0 ? -1 : (int)returnValue[0]); + } + + public override void Flush() + { + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Utils/InterceptableWriteMemoryStream.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Utils/InterceptableWriteMemoryStream.cs new file mode 100644 index 0000000..3d02a16 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Utils/InterceptableWriteMemoryStream.cs @@ -0,0 +1,51 @@ +using System; +using System.IO; + +namespace ESCPOS_NET +{ + public class InterceptableWriteMemoryStream : Stream + { + private MemoryStream _innerStream = new MemoryStream(); + public override bool CanRead => _innerStream.CanRead; + + public override bool CanSeek => _innerStream.CanSeek; + + public override bool CanWrite => _innerStream.CanWrite; + + public override long Length => _innerStream.Length; + + public override long Position { get => _innerStream.Position; set => _innerStream.Position = value; } + + private Action _writeAction { get; set; } + public InterceptableWriteMemoryStream(Action writeAction) + { + _writeAction = writeAction; + } + public override void Flush() + { + _innerStream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _innerStream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return _innerStream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + _innerStream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + var writeArray = new byte[count]; + Buffer.BlockCopy(buffer, offset, writeArray, 0, count); + _writeAction(writeArray); + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Utils/TCPConnection.cs b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Utils/TCPConnection.cs new file mode 100644 index 0000000..55c31f7 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/ESCPOS_NET/Utils/TCPConnection.cs @@ -0,0 +1,59 @@ +using ESCPOS_NET.Utils; +using SimpleTcp; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace ESCPOS_NET +{ + public class TCPConnection + { + public Stream ReadStream { get; private set; } = new EchoStream(); + public Stream WriteStream { get; private set; } + public event EventHandler Connected; + public event EventHandler Disconnected; + public bool IsConnected => _client?.IsConnected ?? false; + private SimpleTcpClient _client; + //public event EventHandler DataReceived; + public TCPConnection(string destination) + { + _client = new SimpleTcpClient(destination); + _client.Events.Connected += ConnectedEventHandler; + _client.Events.Disconnected += DisconnectedEventHandler; + _client.Events.DataReceived += DataReceivedEventHandler; + _client.Keepalive = new SimpleTcpKeepaliveSettings() { EnableTcpKeepAlives = true, TcpKeepAliveInterval = 1, TcpKeepAliveTime = 1, TcpKeepAliveRetryCount = 3 }; + ReadStream.ReadTimeout = 1500; + WriteStream = new InterceptableWriteMemoryStream(bytes => _client.Send(bytes)); + } + private void ConnectedEventHandler(object sender, ClientConnectedEventArgs e) + { + Connected?.Invoke(sender, e); + } + private void DisconnectedEventHandler(object sender, ClientDisconnectedEventArgs e) + { + Disconnected?.Invoke(sender, e); + } + private void DataReceivedEventHandler(object sender, DataReceivedEventArgs e) + { + ReadStream.Write(e.Data, 0, e.Data.Length); + } + public void ConnectWithRetries(int timeoutMs) + { + _client.ConnectWithRetries(timeoutMs); + } + + ~TCPConnection() + { + try + { + _client.Events.DataReceived -= DataReceivedEventHandler; + _client.Events.Connected -= ConnectedEventHandler; + _client.Events.Disconnected -= DisconnectedEventHandler; + _client?.Dispose(); + } + catch { } + } + + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/LICENSE b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/LICENSE new file mode 100644 index 0000000..5631092 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Luke Paireepinart + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/README.md b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/README.md new file mode 100644 index 0000000..196762a --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/README.md @@ -0,0 +1,212 @@ + +

ESCPOS.NET - Easy to use, Cross-Platform, Fast and Efficient.

+
+

+ + + + + + GitHub closed issues + + + Nuget + + + GitHub contributors + +

+ESCPOS.NET is a super easy to use library that supports the most common functionality of the ESC/POS standard by Epson. It is highly compatible, and runs on full framework .NET as well as .NET Core. + +It works with Serial, USB, Ethernet, and WiFi printers, and works great on Windows, Linux and OSX. + +This library is used for thermal receipt printers, line displays, cash drawers, and more! + +ESC/POS is a binary protocol that's a type of "raw" text, which means you do not need drivers to use it. + +This library encompasses helper functions that assist in creating the binary command stream that is needed to control this hardware, as well as the underlying communications that are needed to interface with the hardware. + +This means that Bluetooth, WiFi, Ethernet, USB, and Serial printers are all usable with just this software library and nothing else. + +# Get Started + +## Step 1: Create a Printer object +```csharp +// Ethernet or WiFi +var printer = new NetworkPrinter(ipAddress: "192.168.1.50", port: 9000, reconnectOnTimeout: true); + +// USB, Bluetooth, or Serial +var printer = new SerialPrinter(portName: "COM5", baudRate: 115200); + +// Linux output to USB / Serial file +var printer = new FilePrinter(filePath: "/dev/usb/lp0"); +``` +## Step 1a (optional): Monitor for Events - out of paper, cover open... +```csharp +// Define a callback method. +static void StatusChanged(object sender, EventArgs ps) +{ + var status = (PrinterStatusEventArgs)ps; + Console.WriteLine($"Status: {status.IsPrinterOnline}"); + Console.WriteLine($"Has Paper? {status.IsPaperOut}"); + Console.WriteLine($"Paper Running Low? {status.IsPaperLow}"); + Console.WriteLine($"Cash Drawer Open? {status.IsCashDrawerOpen}"); + Console.WriteLine($"Cover Open? {status.IsCoverOpen}"); +} + +... + +// In your program, register event handler to call the method when printer status changes: +printer.StatusChanged += StatusChanged; + +// and start monitoring for changes. +printer.StartMonitoring(); + +``` + +## Step 2: Write a receipt to the printer +```csharp +var e = new EPSON(); +printer.Write( + ByteSplicer.Combine( + e.CenterAlign(), + e.PrintImage(File.ReadAllBytes("images/pd-logo-300.png"), true), + e.PrintLine(), + e.SetBarcodeHeightInDots(360), + e.SetBarWidth(BarWidth.Default), + e.SetBarLabelPosition(BarLabelPrintPosition.None), + e.PrintBarcode(BarcodeType.ITF, "0123456789"), + e.PrintLine(), + e.PrintLine("B&H PHOTO & VIDEO"), + e.PrintLine("420 NINTH AVE."), + e.PrintLine("NEW YORK, NY 10001"), + e.PrintLine("(212) 502-6380 - (800)947-9975"), + e.SetStyles(PrintStyle.Underline), + e.PrintLine("www.bhphotovideo.com"), + e.SetStyles(PrintStyle.None), + e.PrintLine(), + e.LeftAlign(), + e.PrintLine("Order: 123456789 Date: 02/01/19"), + e.PrintLine(), + e.PrintLine(), + e.SetStyles(PrintStyle.FontB), + e.PrintLine("1 TRITON LOW-NOISE IN-LINE MICROPHONE PREAMP"), + e.PrintLine(" TRFETHEAD/FETHEAD 89.95 89.95"), + e.PrintLine("----------------------------------------------------------------"), + e.RightAlign(), + e.PrintLine("SUBTOTAL 89.95"), + e.PrintLine("Total Order: 89.95"), + e.PrintLine("Total Payment: 89.95"), + e.PrintLine(), + e.LeftAlign(), + e.SetStyles(PrintStyle.Bold | PrintStyle.FontB), + e.PrintLine("SOLD TO: SHIP TO:"), + e.SetStyles(PrintStyle.FontB), + e.PrintLine(" FIRSTN LASTNAME FIRSTN LASTNAME"), + e.PrintLine(" 123 FAKE ST. 123 FAKE ST."), + e.PrintLine(" DECATUR, IL 12345 DECATUR, IL 12345"), + e.PrintLine(" (123)456-7890 (123)456-7890"), + e.PrintLine(" CUST: 87654321"), + e.PrintLine(), + e.PrintLine() + ) +); +``` + + +# Supported Platforms +Desktop support (WiFI, Ethernet, Bluetooth, USB, Serial): +* Windows + - Windows 7+ can support .NET Core or the .NET 471 runtime, and can use this library. +* Linux + - ARM platforms such as Raspberry Pi + - x86/64 platform +* Mac OSX + - Tested on high sierra + +Mobile support (WiFi/Ethernet only): +* iOS + - Xamarin.iOS +* Android + - Xamarin.Android +* Windows + - UWP + +# Supported Hardware +Epson thermal receipt printers are supported, and most common functions such as test printing, styling, alignment, image printing, and barcode printing. + +Generic thermal printers that implement ESC/POS typically work, for example the Royal PT-300, and BemaTech printers are also tested by some members of the community, @juliogamasso and @ivanmontilla. + +Cash Drawers are supported, as are Line Displays. + + +## Further Documentation and Usage +Check out the ESCPOS_NET.ConsoleTest for a comprehensive test suite that covers all implemented functions. + +This package is available on NuGet @ https://www.nuget.org/packages/ESCPOS_NET/ + +Please comment / DM / open issues and let me know how the library is working for you! + +## Contributors +Thanks to all of our contributors working to make this the best .NET thermal printer library out there!! + +* [@lukevp](https://github.com/lukevp) +* [@juliogamasso](https://github.com/juliogamasso) +* [@naaeef](https://github.com/naaeef) +* [@netgg93](https://github.com/netgg93) +* [@igorocampos](https://github.com/igorocampos) +* [@kodejack](https://github.com/kodejack) + +# USB Usage Guide + +For cross-platform support and ease of maintenance, all USB printers are supported over Serial-USB interfaces. These are full-speed and work just as well as native USB as long as you have your port settings optimized. + +On Linux and Mac, USB for Epson printers is exposed as a serial port directly by the os under /dev/ttyusb or something similar based on your platform, and doesn't require drivers. + +On Windows, you must install some type of virtual COM port driver for native USB support, and then map your printer to a specific port, or use a USB-Serial cable and use a serial printer. + +If you have an official Epson printer, the link to install it from Epson is here: https://download.epson-biz.com/modules/pos/index.php?page=single_soft&cid=6481&scat=36&pcat=5 + +If you do not have an official Epson printer, you will have to find a compatible way to expose the USB interface as a virtual Serial port. + +NOTE: The cross platform .NET library we use from Microsoft only supports COM ports 8 and below on windows, so be sure not to use a very high # COM port. + + +# Implemented Commands + +## Bit Image Commands +* `ESC ✻` Select bit-image mode +* `GS ( L` OR `GS 8 L` Set graphics data + * Set the reference dot density for graphics. + * Print the graphics data in the print buffer. + * Store the graphics data in the print buffer (raster format). + +## Character Commands +* `ESC !` Select print mode(s) +* `GS B` Turn white/black reverse print mode on/off - Thanks @juliogamasso! + + +## Print Commands +* `LF` Print and line feed +* `CR` Print and carriage return +* `ESC J` Print and feed paper +* `ESC K` Print and reverse feed +* `ESC d` Print and feed n lines +* `ESC e` Print and reverse feed n lines + +## Bar Code Commands +* `GS H` Select print position of HRI characters +* `GS f` Select font for HRI characters +* `GS h` Set bar code height +* `GS k` Print bar code +* `GS w` Set bar code width + +## Status Commands +* `GS a` Enable/disable Automatic Status Back (ASB) + +## Open Cash Drawer Commands +* `ESC p 0` Open cash drawer pin 2 +* `ESC p 1` Open cash drawer pin 5 + +## Miscellaneous Commands +* `ESC @` Initialize printer diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/banner2.jpg b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/banner2.jpg new file mode 100644 index 0000000..ef31efa Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/banner2.jpg differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/command_list.md b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/command_list.md new file mode 100644 index 0000000..eb17eda --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/command_list.md @@ -0,0 +1,494 @@ +## Line Spacing Commands +* ESC 2 Select default line spacing +* ESC 3 Set line spacing + +## Character Commands +* `ESC SP` Set right-side character spacing +* `ESC –` Turn underline mode on/off +* `ESC E` Turn emphasized mode on/off +* `ESC G` Turn double-strike mode on/off +* `ESC M` Select character font +* `ESC V` Turn 90° clockwise rotation mode on/off +* `ESC r` Select print color +* `ESC t` Select character code table +* `ESC {` Turn upside-down print mode on/off +* `GS !` Select character size +* `GS b` Turn smoothing mode on/off + +## Panel Button Commands +* `ESC c 5` Enable/disable panel buttons + +## Paper Sensor Commands +@@this is for parallel only ESC c 3 Select paper sensor(s) to output paper-end signals +@@this should stop by default ESC c 4 Select paper sensor(s) to stop printing + +## Print Position Commands +* `HT` Horizontal tab +* `ESC $` Set absolute print position +* `ESC D` Set horizontal tab positions +* `ESC a` Select justification +* `GS L` Set left margin +* `GS W` Set print area width + +## Bit Image Commands +* `ESC *` Select bit-image mode + + + +## Mechanism Control Commands +* `ESC U` Turn unidirectional print mode on/off +* `GS V` Select cut mode and cut paper + +## Miscellaneous Commands +* `ESC @` Initialize printer +* `DLE ENQ` Send real-time request to printer +* `DLE DC4 (fn = 1)` Generate pulse in real-time +* `DLE DC4 (fn = 2)` Execute power-off sequence + +TODO: check for DLE DC4 other functions and any other real time commands to make sure they're not part of graphics data because they will be processed immediately. also can use GS ( D to disable realtime commands before processing graphics data + +* `ESC p` Generate pulse +* `GS ( A` Execute test print +* `GS ( D` Enable/disable real-time command +* `GS ( H` Request transmission of response or status +* `GS ( K` Select print control method(s) + * Select the print control mode + * Select the print density + * Select the print speed + * Select the number of parts for the thermal head energizing +* `GS ( P` Page mode control + * Printable area setting when page mode is selected +* `GS ( Q` Commands for drawing graphics + * Draw line + * Draw rectangle + +## Two Dimension Code Commands (QR Codes) +* `GS ( k` Set up and print the symbol + +## Customize Commands +* `GS ( C` Edit NV user memory + * Delete the specified record + * Store the data in the specified record + * Transmit the data in the specified record + * Transmit capacity of the NV user memory currently being used + * Transmit the remaining capacity of the NV user memory + * Transmit the key code list + * Delete all data in the NV user memory +* `GS ( E` Set user setup commands + * Change into the user setting mode + * End the user setting mode session + * Change the memory switch + * Transmit the settings of the memory switch + * Set the customized setting values + * Transmit the customized setting values + * Copy the user-defined page + * Define the data (column format) for the character code page + * Define the data (raster format) for the character code page + * Delete the data for the character code page + * Set the configuration item for the serial interface + * Transmit the configuration item for the serial interface + * Set the configuration item for the Bluetooth interface + * Transmit the configuration item for the Bluetooth interface + * Delete the paper layout + * Set the paper layout + +## Counter Printing Commands + +## Printing Paper Commands + + + + + + + + + + + +## All Commands + +## Print Commands +* LF Print and line feed +* FF (in page mode) Print and return to standard mode (in page mode) +* CR Print and carriage return +* ESC FF Print data in page mode +* ESC J Print and feed paper +* ESC K Print and reverse feed +* ESC d Print and feed n lines +* ESC e Print and reverse feed n lines + +## Line Spacing Commands +* ESC 2 Select default line spacing +* ESC 3 Set line spacing + +## Character Commands +* CAN Cancel print data in page mode +* ESC SP Set right-side character spacing +* ESC ! Select print mode(s) +* ESC % Select/cancel user-defined character set +* ESC & Define user-defined characters +* ESC – Turn underline mode on/off +* ESC ? Cancel user-defined characters +* ESC E Turn emphasized mode on/off +* ESC G Turn double-strike mode on/off +* ESC M Select character font +* ESC R Select an international character set +* ESC V Turn 90° clockwise rotation mode on/off +* ESC r Select print color +* ESC t Select character code table +* ESC { Turn upside-down print mode on/off +* GS ( N Select character effects + * Select character color + * Select background color + * Turn shading mode on/off +* GS ! Select character size +* GS B Turn white/black reverse print mode on/off +* GS b Turn smoothing mode on/off + + +## Panel Button Commands +* ESC c 5 Enable/disable panel buttons + + +## Paper Sensor Commands +* ESC c 3 Select paper sensor(s) to output paper-end signals +* ESC c 4 Select paper sensor(s) to stop printing + +## Print Position Commands +* HT Horizontal tab +* ESC $ Set absolute print position +* ESC D Set horizontal tab positions +* ESC T Select print direction in page mode +* ESC W Set print area in page mode +* ESC \ Set relative print position +* ESC a Select justification +* GS $ Set absolute vertical print position in page mode +* GS L Set left margin +* GS T Set print position to the beginning of print line +* GS W Set print area width +* GS \ Set relative vertical print position in page mode + +## Bit Image Commands +* ESC ✻ Select bit-image mode +* FS p Print NV bit image +* GS ( L GS 8 L Set graphics data + * Transmit the NV graphics memory capacity. + * Set the reference dot density for graphics. + * Print the graphics data in the print buffer. + * Transmit the remaining capacity of the NV graphics memory. + * Transmit the remaining capacity of the download graphics memory. + * Transmit the key code list for defined NV graphics. + * Delete all NV graphics data. + * Delete the specified NV graphics data. + * Define the NV graphics data (raster format). + * Define the NV graphics data (column format). + * Print the specified NV graphics data. + * Transmit the key code list for defined download graphics. + * Delete all NV graphics data. + * Delete the specified download graphics data. + * Define the downloaded graphics data (raster format). + * Define the downloaded graphics data (column format). + * Print the specified download graphics data. + * Store the graphics data in the print buffer (raster format). + * Store the graphics data in the print buffer (column format). +* FS q Define NV bit image +* GS v 0 Print raster bit image +* GS ✻ Define downloaded bit image +* GS / Print downloaded bit image +* GS Q 0 Print variable vertical size bit image + +## Status Commands +* DLE EOT Transmit real-time status +* ESC u Transmit peripheral device status +* ESC v Transmit paper sensor status +* GS a Enable/disable Automatic Status Back (ASB) +* GS j Enable/disable Automatic Status Back (ASB) for ink +* GS r Transmit status + +## Bar Code Commands +* GS H Select print position of HRI characters +* GS f Select font for HRI characters +* GS h Set bar code height +* GS k Print bar code +* GS w Set bar code width + +## Macro Function Commands +* GS : Start/end macro definition +* GS ^ Execute macro + +## Mechanism Control Commands +* ESC < Return home +* ESC U Turn unidirectional print mode on/off +* ESC i Partial cut (one point left uncut) +* ESC m Partial cut (three points left uncut) +* GS V Select cut mode and cut paper + +## Miscellaneous Commands +* DLE ENQ Send real-time request to printer +* DLE DC4 (fn = 1) Generate pulse in real-time +* DLE DC4 (fn = 2) Execute power-off sequence +* DLE DC4 (fn = 7) Transmit specified status in real time +* DLE DC4 (fn = 8) Clear buffer (s) +* ESC ( A Control beeper tones + * Beep integrated beeper in TM-U230 models + * Set integrated beeper when offline factors occur in TM-U230 models + * Set integrated beeper except when offline factors occur in TM-U230 models +* ESC = Select peripheral device +* ESC @ Initialize printer +* ESC L Select page mode +* ESC S Select standard mode +* ESC p Generate pulse +* GS ( A Execute test print +* GS ( D Enable/disable real-time command +* GS ( H Request transmission of response or status +* GS ( K Select print control method(s) + * Select the print control mode + * Select the print density + * Select the print speed + * Select the number of parts for the thermal head energizing +* GS ( P Page mode control + * Printable area setting when page mode is selected +* GS ( Q Commands for drawing graphics + * Draw line + * Draw rectangle + +## Kanji Commands +* FS ! Select print mode(s) for Kanji characters +* FS & Select Kanji character mode +* FS ( A Select Kanji character style(s) +* Select Kanji character font +* FS – Turn underline mode on/off for Kanji characters +* FS . Cancel Kanji character mode +* FS 2 Define user-defined Kanji characters +* FS C Select Kanji character code system +* FS S Set Kanji character spacing +* FS W Turn quadruple-size mode on/off for Kanji characters +* FS ? Cancel user-defined Kanji characters + +## Two Dimension Code Commands (QR Codes) +* GS ( k Set up and print the symbol + +## Customize Commands +* FS g 1 Write to NV user memory +* FS g 2 Read from NV user memory +* GS ( C Edit NV user memory + * Delete the specified record + * Store the data in the specified record + * Transmit the data in the specified record + * Transmit capacity of the NV user memory currently being used + * Transmit the remaining capacity of the NV user memory + * Transmit the key code list + * Delete all data in the NV user memory +* GS ( E Set user setup commands + * Change into the user setting mode + * End the user setting mode session + * Change the memory switch + * Transmit the settings of the memory switch + * Set the customized setting values + * Transmit the customized setting values + * Copy the user-defined page + * Define the data (column format) for the character code page + * Define the data (raster format) for the character code page + * Delete the data for the character code page + * Set the configuration item for the serial interface + * Transmit the configuration item for the serial interface + * Set the configuration item for the Bluetooth interface + * Transmit the configuration item for the Bluetooth interface + * Delete the paper layout + * Set the paper layout + +## Counter Printing Commands +* GS C 0 Select counter print mode +* GS C 1 Select count mode (A) +* GS C 2 Set counter +* GS C ; Select counter mode (B) +* GS c Print counter + +## Printing Paper Commands +* FS ( L Select label and black mark control function(s) + * Paper layout setting + * Paper layout information transmission + * Transmit the positioning information + * Feed paper to the label peeling position + * Feed paper to the cutting position + * Feed paper to the print starting position + * Paper layout error special margin setting + + + + +## Mechanism Control Commands + + + + + + +## Graphics Commands + +## NV Graphics Commands + + +## Download Graphics Commands + +## Logo Print Commands + +## Bar Code Commands + +## Two-Dimensional Code Commands + +## Status Commands + +## Macro Function Commands + +## Miscellaneous Commands + + +### User Setup Commands + +TODO: +* Line Spacing +* SetLineSpacing() +* SetLineSpacing(n dots) 0 <= n <= 255 + +Not In Scope: +* Drawer Kick Out Commands +* Page Mode Commands + + + + +# COMMANDS IN ALPHANUMERIC ORDER +Command|Name|Function type +---|---|--- +HT |Horizontal tab |PRINT POSITION COMMANDS +LF |Print and line feed |PRINT COMMANDS +FF (in page mode) |Print and return to standard mode| PRINT COMMANDS +CR |Print and carriage return |PRINT COMMANDS +CAN |Cancel print data in page mode |CHARACTER COMMANDS +DLE EOT |Real-time status transmission |STATUS COMMANDS +DLE ENQ |Real-time request to printer| MISCELLANEOUS COMMANDS +DLE DC4 (fn = 1) |Generate pulse at real-time |MISCELLANEOUS COMMANDS +DLE DC4 (fn = 2) |Turn off the power| MISCELLANEOUS COMMANDS +DLE DC4 (fn = 7) |Transmit specified status in real time |MISCELLANEOUS COMMANDS +DLE DC4 (fn = 8) |Clear buffer| MISCELLANEOUS COMMANDS +ESC FF |Print data in page mode |PRINT COMMANDS +ESC SP |Set right-side character spacing |CHARACTER COMMANDS +ESC ! |Select print mode(s) |CHARACTER COMMANDS +ESC $ |Set absolute print position |PRINT POSITION COMMANDS +ESC % |Select/cancel user-defined character set| CHARACTER COMMANDS +ESC & |Define user-defined characters |CHARACTER COMMANDS +ESC ( A |Control of the beeper |MISCELLANEOUS COMMANDS +ESC ✻ |Select bit-image mode| BIT-IMAGE COMMANDS +ESC – |Turn underline mode on/off| CHARACTER COMMANDS +ESC 2 |Select default line spacing| LINE SPACING COMMANDS +ESC 3 |Set line spacing |LINE SPACING COMMANDS +ESC < |Return home| MECHANISM CONTROL COMMANDS +ESC = |Select peripheral device |MISCELLANEOUS COMMANDS +ESC ? |Cancel user-defined characters| CHARACTER COMMANDS +ESC @ |Initialize printer| MISCELLANEOUS COMMANDS +ESC D |Set horizontal tab positions |PRINT POSITION COMMANDS +ESC E |Turn emphasized mode on/off |CHARACTER COMMANDS +ESC G |Turn double-strike mode on/off| CHARACTER COMMANDS +ESC J |Print and feed paper |PRINT COMMANDS +ESC K |Print and reverse feed |PRINT COMMANDS +ESC L |Select page mode |MISCELLANEOUS COMMANDS +ESC M |Select character font| CHARACTER COMMANDS +ESC R |Select an international character set| CHARACTER COMMANDS +ESC S |Select standard mode |MISCELLANEOUS COMMANDS +ESC T |Select print direction in page mode |PRINT POSITION COMMANDS +ESC U |Turn unidirectional printing mode on/off |MECHANISM CONTROL COMMANDS +ESC V |Turn 90° clockwise rotation mode on/off |CHARACTER COMMANDS +ESC W |Set printing area in page mode |PRINT POSITION COMMANDS +ESC \ |Set relative print position| PRINT POSITION COMMANDS +ESC a |Select justification |PRINT POSITION COMMANDS +ESC c 3 |Select paper sensor(s) to output paper-end signals |PAPER SENSOR COMMANDS +ESC c 4 |Select paper sensor(s) to stop printing |PAPER SENSOR COMMANDS +ESC c 5 |Enable/disable panel buttons| PANEL BUTTON COMMAND +ESC d |Print and feed n lines |PRINT COMMANDS +ESC e |Print and reverse feed n lines |PRINT COMMANDS +ESC i |Partial cut (one point left uncut) |MECHANISM CONTROL COMMANDS +ESC m |Partial cut (three points left uncut) |MECHANISM CONTROL COMMANDS +ESC p |Generate pulse| MISCELLANEOUS COMMANDS +ESC r |Select print color| CHARACTER COMMANDS +ESC t |Select character code table |CHARACTER COMMANDS +ESC u |Transmit peripheral device status| STATUS COMMANDS +ESC { |Turn upside-down printing mode on/off| CHARACTER COMMANDS +FS ! |Set print mode(s) for Kanji characters |KANJI COMMANDS +FS & |Select Kanji character mode |KANJI COMMANDS +FS ( A |Define character effects of Kanji characters. |KANJI COMMANDS +FS ( L |Control of the label paper / black mark paper |PRINTING PAPER COMMANDS +FS – |Turn underline mode on/off for Kanji characters |KANJI COMMANDS +FS . |Cancel Kanji character mode |KANJI COMMANDS +FS 2 |Define user-defined Kanji characters| KANJI COMMANDS +FS C |Select Kanji character code system |KANJI COMMANDS +FS S |Set Kanji character spacing |KANJI COMMANDS +FS W |Turn quadruple-size mode on/off for Kanji characters| KANJI COMMANDS +FS ? |Cancel user-defined Kanji characters |KANJI COMMANDS +FS g 1 |Write to NV user memory |CUSTOMIZE COMMANDS +FS g 2 |Read from NV user memory |CUSTOMIZE COMMANDS +FS p |Print NV bit image |BIT-IMAGE COMMANDS +FS q |Define NV bit image |BIT-IMAGE COMMANDS +GS ! |Select character size |CHARACTER COMMANDS +GS $ |Set absolute vertical print position in page mode| PRINT POSITION COMMANDS +GS ( A |Execute test print| MISCELLANEOUS COMMANDS +GS ( C |Edit NV user memory |CUSTOMIZE COMMANDS +GS ( D |Enable/disable real-time command| MISCELLANEOUS COMMANDS +GS ( E |User setup commands |CUSTOMIZE COMMANDS +GS ( H |Require response transmission |MISCELLANEOUS COMMANDS +GS ( K |Select printing control| MISCELLANEOUS COMMANDS +GS ( L GS 8 L |Specify graphics data| BIT-IMAGE COMMANDS +GS ( M |Customize printer control value(s)| CUSTOMIZE COMMANDS +GS ( N |Select character effects| CHARACTER COMMANDS +GS ( k |Specify and print the symbol |TWO DIMENSION CODE COMMANDS +GS ✻ |Define downloaded bit image |BIT-IMAGE COMMANDS +GS / |Print downloaded bit image |BIT-IMAGE COMMANDS +GS : |Start/end macro definition |MACRO FUNCTION COMMANDS +GS B |Turn white/black reverse printing mode on/off| CHARACTER COMMANDS +GS C 0 |Select counter print mode| COUNTER PRINTING COMMANDS +GS C 1 |Select count mode (A) |COUNTER PRINTING COMMANDS +GS C 2 |Select counter print mode| COUNTER PRINTING COMMANDS +GS C ; |Select counter mode (B) |COUNTER PRINTING COMMANDS +GS H |Select printing position of HRI characters |BAR CODE COMMANDS +GS I |Transmit printer |ID MISCELLANEOUS COMMANDS +GS L |Set left margin |PRINT POSITION COMMANDS +GS P |Set horizontal and vertical motion units| MISCELLANEOUS COMMANDS +GS Q 0 |Print variable vertical size bit image |BIT-IMAGE COMMANDS +GS T |Set print position to the beginning of print line| PRINT POSITION COMMANDS +GS V |Select cut mode and cut paper |MECHANISM CONTROL COMMANDS +GS W |Set printing area width PRINT |POSITION COMMANDS +GS \ |Set relative vertical print position in page mode| PRINT POSITION COMMANDS +GS ^ |Execute macro |MACRO FUNCTION COMMANDS +GS a |Enable/disable Automatic Status Back (ASB) |STATUS COMMANDS +GS b |Turn smoothing mode on/off |CHARACTER COMMANDS +GS c |Print counter| COUNTER PRINTING COMMANDS +GS f |Select font for HRI characters |BAR CODE COMMANDS +GS g 0 |Initialize maintenance counter| MISCELLANEOUS COMMANDS +GS g 2 |Transmit maintenance counter |MISCELLANEOUS COMMANDS +GS h |Set bar code height |BAR CODE COMMANDS +GS j |Enable/disable Ink Automatic Status Back (ASB) |STATUS COMMANDS +GS k |Print bar code |BAR CODE COMMANDS +GS r |Transmit status |STATUS COMMANDS +GS v 0 |Print raster bit image |BIT-IMAGE COMMANDS +GS w |Set bar code width |BAR CODE COMMANDS +GS z 0 |Set online recovery wait time| MISCELLANEOUS COMMANDS +GS z 0 |(TM-L90 w/ Peeler) Set online recovery wait time| MISCELLANEOUS COMMANDS + +## The following commands are supported only by Simplified Chinese/Traditional Chinese/Japanese models. + +Command|Name|Function type +---|---|--- +FS ! |Set print mode(s) for Kanji characters |KANJI COMMANDS +FS & |Select Kanji character mode |KANJI COMMANDS +FS ( A |Define character effects of Kanji characters |KANJI COMMANDS +FS – |Turn underline mode on/off for Kanji characters |KANJI COMMANDS +FS . |Cancel Kanji character mode |KANJI COMMANDS +FS 2 |Define user-defined Kanji characters| KANJI COMMANDS +FS C |Select Kanji character code system |KANJI COMMANDS +FS S |Set Kanji character spacing| KANJI COMMANDS +FS W |Turn quadruple-size mode on/off for Kanji characters |KANJI COMMANDS +FS ? |Cancel user-defined Kanji characters| KANJI COMMANDS + + + diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/nuget.exe b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/nuget.exe new file mode 100644 index 0000000..e00ef51 Binary files /dev/null and b/PointOfSale/Pos.Ui/EpsonPrinter/ESC-POS/ESC-POS-.NET-master/nuget.exe differ diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Enums/AlignmentEnum.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Enums/AlignmentEnum.cs new file mode 100644 index 0000000..4efe3a3 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Enums/AlignmentEnum.cs @@ -0,0 +1,9 @@ +namespace EpsonPrinter.Enums +{ + public enum AlignmentEnum + { + Left, + Center, + Right + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Enums/PrintStylesEnum.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Enums/PrintStylesEnum.cs new file mode 100644 index 0000000..597669b --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Enums/PrintStylesEnum.cs @@ -0,0 +1,12 @@ +namespace EpsonPrinter.Enums +{ + public enum PrintStylesEnum + { + None, + FontB, + Bold, + DoubleHeight, + DoubleWidth, + UnderLine + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/EpsonPrinter.csproj b/PointOfSale/Pos.Ui/EpsonPrinter/EpsonPrinter.csproj new file mode 100644 index 0000000..aeb893c --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/EpsonPrinter.csproj @@ -0,0 +1,24 @@ + + + + net6.0 + Linux + ..\Pos + + + + + + + + + + + + + + + + + + diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Model/BodyModel.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Model/BodyModel.cs new file mode 100644 index 0000000..7e89768 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Model/BodyModel.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace EpsonPrinter.Model +{ + public class BodyModel + { + public List Products { get; set; } + public decimal TotalPrice { get; set; } + public decimal TotalVat { get; set; } + public int ReceiptNumber { get; set; } + public string ReceiptTime { get; set; } + public string Staff { get; set; } + + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Model/BodyProductModel.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Model/BodyProductModel.cs new file mode 100644 index 0000000..00d72aa --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Model/BodyProductModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace EpsonPrinter.Model +{ + public class BodyProductModel + { + public string Product { get; set; } + public int NoOfProduct { get; set; } + public decimal Price { get; set; } + public decimal TotalPrice { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Model/FooterModel.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Model/FooterModel.cs new file mode 100644 index 0000000..d943e1c --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Model/FooterModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Enums; + +namespace EpsonPrinter.Model +{ + public class FooterModel + { + public string Value { get; set; } + public PrintStyleModel PrintStyles { get; set; } + public AlignmentEnum TextAlignment { get; set; } + public int FeedingLines { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Model/HeaderModel.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Model/HeaderModel.cs new file mode 100644 index 0000000..37e9550 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Model/HeaderModel.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Enums; +using ESCPOS_NET.Emitters; + +namespace EpsonPrinter.Model +{ + public class HeaderModel + { + public string Value { get; set; } + public PrintStyleModel PrintStyles { get; set; } + public AlignmentEnum TextAlignment { get; set; } + public int FeedingLines { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Model/PrintStyleModel.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Model/PrintStyleModel.cs new file mode 100644 index 0000000..853dc5c --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Model/PrintStyleModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace EpsonPrinter.Model +{ + public class PrintStyleModel + { + public bool FontB { get; set; } + public bool Bold { get; set; } + public bool DoubleHeight { get; set; } + public bool DoubleWidth { get; set; } + public bool Underline { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Model/ReceiptModel.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Model/ReceiptModel.cs new file mode 100644 index 0000000..04f486b --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Model/ReceiptModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace EpsonPrinter.Model +{ + public class ReceiptModel + { + public string LogoBase64 { get; set; } + public List Header { get; set; } + public BodyModel BodyModel { get; set; } + public List Footer { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Model/SaleOfDayDetail.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Model/SaleOfDayDetail.cs new file mode 100644 index 0000000..f0b67c1 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Model/SaleOfDayDetail.cs @@ -0,0 +1,10 @@ +using System; + +namespace EpsonPrinter.Model +{ + public class SaleOfDayDetail + { + public string Category { get; set; } + public decimal TotalSale { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Model/SaleOfDayModel.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Model/SaleOfDayModel.cs new file mode 100644 index 0000000..c50f634 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Model/SaleOfDayModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace EpsonPrinter.Model +{ + public class SaleOfDayModel + { + public DateTime Date { get; set; } + public int TotalCustomers { get; set; } + public Decimal TotalSale { get; set; } + public List SaleOfDayDetail { get; set; } = new(); + + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Program.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Program.cs new file mode 100644 index 0000000..85c4e64 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Program.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace EpsonPrinter +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Properties/launchSettings.json b/PointOfSale/Pos.Ui/EpsonPrinter/Properties/launchSettings.json new file mode 100644 index 0000000..896e040 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:23653", + "sslPort": 44334 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "EpsonPrinter": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Services/EpsonPrintService.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Services/EpsonPrintService.cs new file mode 100644 index 0000000..1dba4e0 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Services/EpsonPrintService.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Model; +using ESCPOS_NET; +using ESCPOS_NET.Emitters; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json.Linq; + +namespace EpsonPrinter.Services +{ + public class EpsonPrintService + { + private readonly SerialPrinter _serialPrinter; + private readonly IConfiguration _config; + + public EpsonPrintService(IConfiguration config) + { + _config = config; + string comPort = _config["PrintSettings:ComPort"]; + int baudRate = Convert.ToInt32(_config["PrintSettings:BaudRate"]); + _serialPrinter = new SerialPrinter(comPort,baudRate); + + } + + private void PrintReceipt(ReceiptModel model) + { + + } + + + public void PrintSaleOfDay(SaleOfDayModel model) + { + ICommandEmitter e = new EPSON(); + Init(e); + PrintSaleOfDay(model,e); + } + + private void Init(ICommandEmitter e) + { + _serialPrinter.Write(e.Initialize()); + _serialPrinter.Write(e.Enable()); + _serialPrinter.Write(e.CodePage(CodePage.ISO8859_15_LATIN9)); + _serialPrinter.Write(e.EnableAutomaticStatusBack()); + } + + //private void PrintReceipt(ReceiptModel model, ICommandEmitter e) + //{ + // List printBytes = new List(); + // PrintHeader printHeader = new PrintHeader(e); + // printBytes.AddRange(printHeader.Print(model.Header)); + + // PrintBody printBody = new PrintBody(e,_config); + // printBytes.AddRange(printBody.Print(model.BodyModel)); + + // PrintFooter printFooter = new PrintFooter(e); + // printBytes.AddRange(printFooter.Print(model.Footer)); + + // //And at last, print + // byte[][] array = printBytes.SelectMany(c => c).ToArray(); + + // _serialPrinter.Write(array); + // _serialPrinter.Write(e.FeedLines(5)); + // _serialPrinter.Write(e.FullCut()); + + //} + private void PrintSaleOfDay(SaleOfDayModel model, ICommandEmitter e) + { + List data = new List(); + + int printWidth = Convert.ToInt32(_config["PrintSettings:PrintWidth"])-8; + PrintString printString = new PrintString(); + + byte[][] dateBytes = + { + e.SetStyles(PrintStyle.DoubleWidth), + e.LeftAlign(), + e.PrintLine($"Dato: {model.Date:dd-MM-yyyy}") + }; + data.Add(dateBytes); + + byte[][] seperater = + { + e.SetStyles(PrintStyle.None), + e.LeftAlign(), + e.PrintLine($"-----------------------------------") + }; + data.Add(seperater); + + + foreach (SaleOfDayDetail saleOfDayDetail in model.SaleOfDayDetail) + { + //string pS = printString.MakePrintString(printWidth, saleOfDayDetail.Category, saleOfDayDetail.TotalSale.ToString(CultureInfo.InvariantCulture)); + string pS = $"{saleOfDayDetail.Category} : {saleOfDayDetail.TotalSale.ToString("#.#0")}"; + byte[][] totalPrice = + { + e.SetStyles(PrintStyle.DoubleWidth), + e.LeftAlign(), + e.PrintLine(pS) + }; + data.Add(totalPrice); + } + + data.Add(seperater); + + //string totalSale = printString.MakePrintString(printWidth, "Salg", model.TotalSale.ToString(CultureInfo.InvariantCulture)); + string totalSale = $"Total salg: {model.TotalSale.ToString("#.#0")}"; + byte[][] tSBytes = + { + e.SetStyles(PrintStyle.DoubleWidth), + e.LeftAlign(), + e.PrintLine(totalSale) + }; + data.Add(tSBytes); + + //string totalCustomer = printString.MakePrintString(printWidth, "Kunder", model.TotalCustomers.ToString()); + string totalCustomer = $"Antal kunder: {model.TotalCustomers}"; + byte[][] tCBytes = + { + e.SetStyles(PrintStyle.DoubleWidth), + e.LeftAlign(), + e.PrintLine(totalCustomer) + }; + data.Add(tCBytes); + + byte[][] array = data.SelectMany(c => c).ToArray(); + _serialPrinter.Write(array); + _serialPrinter.Write(e.FeedLines(5)); + _serialPrinter.Write(e.FullCut()); + } + + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintAlignment.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintAlignment.cs new file mode 100644 index 0000000..4a7ad9f --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintAlignment.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Input; +using EpsonPrinter.Enums; +using ESCPOS_NET.Emitters; + +namespace EpsonPrinter.Services +{ + public class PrintAlignment + { + public byte[] GetTextAlignment(ICommandEmitter e, AlignmentEnum alignment) + { + if (alignment == AlignmentEnum.Left) + return e.LeftAlign(); + + if (alignment == AlignmentEnum.Right) + return e.RightAlign(); + + return e.CenterAlign(); + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintBody.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintBody.cs new file mode 100644 index 0000000..afb1d26 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintBody.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Model; +using ESCPOS_NET.Emitters; +using Microsoft.Extensions.Configuration; + +namespace EpsonPrinter.Services +{ + public class PrintBody + { + private ICommandEmitter _e; + private IConfiguration _config; + + private int productMaxWidth; + + public PrintBody(ICommandEmitter e, IConfiguration config) + { + _e = e; + _config = config; + } + + public List Print(BodyModel model) + { + List data = new List(); + + byte[][] headerModel = + { + _e.SetStyles(PrintStyle.None), + _e.LeftAlign(), + _e.PrintLine($"Dato.........: {model.ReceiptTime}"), + _e.PrintLine($"Ekspedient...: {model.Staff}"), + _e.PrintLine($"Kvittering...: {model.ReceiptNumber}"), + _e.FeedLines(1) + }; + data.Add(headerModel); + + int printWidth = Convert.ToInt32(_config["PrintSettings:PrintWidth"]); + foreach (BodyProductModel product in model.Products) + { + //Size of product is max half of recipt + string totalProductLine = MakeProductNameString(product.Product, printWidth); + totalProductLine += MakeProductNumber(product.NoOfProduct, product.Price); + totalProductLine = MakeTotalProductPrice(totalProductLine, product.TotalPrice,printWidth); + + byte[][] p = + { + _e.PrintLine(totalProductLine) + }; + data.Add(p); + } + + PrintString printString = new PrintString(); + string totalPricePrint = printString.MakePrintString(printWidth, $"Total: ", $"{model.TotalPrice:F} DKK"); + byte[][] totalPrice = + { + _e.FeedLines(1), + _e.SetStyles(PrintStyle.Bold | PrintStyle.DoubleHeight), + _e.RightAlign(), + _e.PrintLine(totalPricePrint) + }; + data.Add(totalPrice); + + byte[][] vat = + { + _e.FeedLines(1), + _e.SetStyles(PrintStyle.None), + _e.LeftAlign(), + _e.PrintLine(MakeVat(printWidth,model.TotalVat)) + }; + data.Add(vat); + + return data; + } + + private string MakeProductNameString(string product, int totalWidth) + { + string productString = product; + productMaxWidth = (totalWidth / 2) - 5; + int productLength = product.Length; + if (productLength < productMaxWidth) + { + + for (int i = productLength; i < productMaxWidth; i++) + { + productString += "."; + } + } + + return productString; + } + + private string MakeProductNumber(int noOfProduct, decimal price) + { + return $"{noOfProduct} á {price:F} DKK :"; + } + + private string MakeTotalProductPrice(string buffer, decimal totalPrice, int printWidth) + { + PrintString printString = new PrintString(); + string p = $"{totalPrice:F} DKK"; + string makePrintString = printString.MakePrintString(printWidth, buffer, p); + return makePrintString; + } + + private string MakeVat(int printWidth, decimal vat) + { + PrintString printString = new PrintString(); + string vatDesc = $"Moms udgør 25% :"; + string v = $"{vat:F} DKK"; + string vatString = printString.MakePrintString(printWidth, vatDesc, v); + return vatString; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintFooter.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintFooter.cs new file mode 100644 index 0000000..d23e8f4 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintFooter.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Model; +using ESCPOS_NET; +using ESCPOS_NET.Emitters; + +namespace EpsonPrinter.Services +{ + public class PrintFooter + { + private ICommandEmitter _e; + + public PrintFooter(ICommandEmitter e) + { + _e = e; + } + + public List Print(List model) + { + List data = new List(); + PrintStyleCombination printStyleCombination = new PrintStyleCombination(); + PrintAlignment printAlignment = new PrintAlignment(); + byte[][] feedLines = + { + _e.FeedLines(1) + }; + data.Add(feedLines); + foreach (FooterModel footerModel in model) + { + + PrintStyle printStyle = printStyleCombination.Combine(footerModel.PrintStyles); + byte[][] bytes = { + _e.SetStyles(PrintStyle.None), + printAlignment.GetTextAlignment(_e,footerModel.TextAlignment), + _e.SetStyles(printStyle), + _e.PrintLine(footerModel.Value), + _e.FeedLines(footerModel.FeedingLines) + }; + data.Add(bytes); + } + + return data; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintHeader.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintHeader.cs new file mode 100644 index 0000000..eec3fd3 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintHeader.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using EpsonPrinter.Model; +using ESCPOS_NET; +using ESCPOS_NET.Emitters; +using ESCPOS_NET.Utilities; + +namespace EpsonPrinter.Services +{ + public class PrintHeader + { + private ICommandEmitter _e; + + public PrintHeader(ICommandEmitter e) + { + _e = e; + } + + public List Print(List model) + { + List data = new List(); + PrintStyleCombination printStyleCombination = new PrintStyleCombination(); + PrintAlignment printAlignment = new PrintAlignment(); + foreach (HeaderModel headerModel in model) + { + + PrintStyle printStyle = printStyleCombination.Combine(headerModel.PrintStyles); + byte[][] bytes = { + _e.SetStyles(PrintStyle.None), + printAlignment.GetTextAlignment(_e,headerModel.TextAlignment), + _e.SetStyles(printStyle), + _e.PrintLine(headerModel.Value), + _e.FeedLines(headerModel.FeedingLines) + }; + data.Add(bytes); + } + byte[][] feedLines = + { + _e.FeedLines(1) + }; + data.Add(feedLines); + return data; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintString.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintString.cs new file mode 100644 index 0000000..fb664a1 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintString.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace EpsonPrinter.Services +{ + public class PrintString + { + /// + /// An appropriate interval is converted into the length of + /// the tab about two texts. And make a printing data. + /// + /// + /// The width of the territory which it prints on is converted into the number of + /// characters, and that value is specified. + /// + /// + /// It is necessary as an information for deciding the interval of the text. + /// + /// + /// It is necessary as an information for deciding the interval of the text, too. + /// + /// printing data. + /// + public String MakePrintString(int iLineChars, String strBuf, String strPrice) + { + int iSpaces = 0; + String tab = ""; + try + { + iSpaces = iLineChars - (strBuf.Length + strPrice.Length); + for (int j = 0; j < iSpaces; j++) + { + tab += " "; + } + } + catch (Exception) + { + } + return strBuf + tab + strPrice; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintStyleCombination.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintStyleCombination.cs new file mode 100644 index 0000000..bd6adfd --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Services/PrintStyleCombination.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Enums; +using EpsonPrinter.Model; +using ESCPOS_NET.Emitters; + +namespace EpsonPrinter.Services +{ + public class PrintStyleCombination + { + public PrintStyle Combine(PrintStyleModel printStyleModel) + { + + //Bold + if (printStyleModel.Bold && !printStyleModel.DoubleHeight && !printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.Bold; + + if (printStyleModel.Bold && printStyleModel.DoubleHeight && !printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.Bold | PrintStyle.DoubleHeight; + + if(printStyleModel.Bold && printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.Bold | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth; + + if (printStyleModel.Bold && printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.Bold | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.FontB; + + if (printStyleModel.Bold && printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && printStyleModel.FontB && printStyleModel.Underline) + return PrintStyle.Bold | PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.FontB | PrintStyle.Underline; + + //DoubleHeight + if (printStyleModel.DoubleHeight && !printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.DoubleHeight; + + if (printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.DoubleHeight | PrintStyle.DoubleWidth; + + if (printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.FontB; + + if (printStyleModel.DoubleHeight && printStyleModel.DoubleWidth && printStyleModel.FontB && printStyleModel.Underline) + return PrintStyle.DoubleHeight | PrintStyle.DoubleWidth | PrintStyle.FontB | PrintStyle.Underline; + + //DoubleWidth + if (printStyleModel.DoubleWidth && !printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.DoubleWidth; + + if (printStyleModel.DoubleWidth && printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.DoubleWidth | PrintStyle.FontB; + + if (printStyleModel.DoubleWidth && printStyleModel.FontB && printStyleModel.Underline) + return PrintStyle.DoubleWidth | PrintStyle.FontB | PrintStyle.Underline; + + //FontB + if (printStyleModel.FontB && !printStyleModel.Underline) + return PrintStyle.FontB; + + if(printStyleModel.FontB && printStyleModel.Underline) + return PrintStyle.FontB | PrintStyle.Underline; + + //Underline + if (printStyleModel.Underline) + return PrintStyle.Underline; + + return PrintStyle.None; + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/Startup.cs b/PointOfSale/Pos.Ui/EpsonPrinter/Startup.cs new file mode 100644 index 0000000..8278a63 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/Startup.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using EpsonPrinter.Services; +using ESCPOS_NET; + +namespace EpsonPrinter +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + + services.AddControllers(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "EpsonPrinter", Version = "v1" }); + }); + + services.AddSingleton(); + + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI( + c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "EpsonPrinter v1")); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/appsettings.Development.json b/PointOfSale/Pos.Ui/EpsonPrinter/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/appsettings.json b/PointOfSale/Pos.Ui/EpsonPrinter/appsettings.json new file mode 100644 index 0000000..1f90b74 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + "PrintSettings": { + "ComPort": "COM5", + "BaudRate": 115200, + "PrintWidth": 48 + } +} diff --git a/PointOfSale/Pos.Ui/EpsonPrinter/testdata.json b/PointOfSale/Pos.Ui/EpsonPrinter/testdata.json new file mode 100644 index 0000000..2d10382 --- /dev/null +++ b/PointOfSale/Pos.Ui/EpsonPrinter/testdata.json @@ -0,0 +1,73 @@ +{ + "logoBase64": "string", + "header": [ + { + "value": "Frste linie med ", + "printStyles": { + "fontB": false, + "bold": true, + "doubleHeight": false, + "doubleWidth": false, + "underline": false + }, + "textAlignment": 0 + }, + { + "value": "Anden linie", + "printStyles": { + "fontB": false, + "bold": true, + "doubleHeight": false, + "doubleWidth": false, + "underline": false + }, + "textAlignment": 0 + }, + { + "value": "Tredje linie", + "printStyles": { + "fontB": false, + "bold": true, + "doubleHeight": false, + "doubleWidth": false, + "underline": false + }, + "textAlignment": 0 + }, + { + "value": "Fjerde linie", + "printStyles": { + "fontB": false, + "bold": true, + "doubleHeight": false, + "doubleWidth": false, + "underline": false + }, + "textAlignment": 0 + }, + { + "value": "Femte linie", + "printStyles": { + "fontB": false, + "bold": true, + "doubleHeight": false, + "doubleWidth": false, + "underline": false + }, + "textAlignment": 0 + } + ], + "footer": [ + { + "value": "string", + "printStyles": { + "fontB": true, + "bold": true, + "doubleHeight": true, + "doubleWidth": true, + "underline": true + }, + "textAlignment": 0 + } + ] +} \ No newline at end of file diff --git a/PointOfSale/Pos.Ui/Pos/.config/dotnet-tools.json b/PointOfSale/Pos.Ui/Pos/.config/dotnet-tools.json new file mode 100644 index 0000000..06b8682 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "5.0.9", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/PointOfSale/Pos.Ui/Pos/App.xaml b/PointOfSale/Pos.Ui/Pos/App.xaml new file mode 100644 index 0000000..9efbbcd --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/App.xaml.cs b/PointOfSale/Pos.Ui/Pos/App.xaml.cs new file mode 100644 index 0000000..46101cf --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/App.xaml.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace Pos +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + public App() + { + //Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("NDk1NTE0QDMxMzkyZTMyMmUzMFI1bUkyeWZBZngydzlUaHJpUEdiNmtnWnhFUytPYUVvYnQvZFErWXdkN3c9"); + } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/AssemblyInfo.cs b/PointOfSale/Pos.Ui/Pos/AssemblyInfo.cs new file mode 100644 index 0000000..8b5504e --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/PointOfSale/Pos.Ui/Pos/Icons/cashier.png b/PointOfSale/Pos.Ui/Pos/Icons/cashier.png new file mode 100644 index 0000000..e6b672a Binary files /dev/null and b/PointOfSale/Pos.Ui/Pos/Icons/cashier.png differ diff --git a/PointOfSale/Pos.Ui/Pos/Icons/category.png b/PointOfSale/Pos.Ui/Pos/Icons/category.png new file mode 100644 index 0000000..85eca0e Binary files /dev/null and b/PointOfSale/Pos.Ui/Pos/Icons/category.png differ diff --git a/PointOfSale/Pos.Ui/Pos/Icons/employee.png b/PointOfSale/Pos.Ui/Pos/Icons/employee.png new file mode 100644 index 0000000..63d317c Binary files /dev/null and b/PointOfSale/Pos.Ui/Pos/Icons/employee.png differ diff --git a/PointOfSale/Pos.Ui/Pos/Icons/employees.png b/PointOfSale/Pos.Ui/Pos/Icons/employees.png new file mode 100644 index 0000000..631de69 Binary files /dev/null and b/PointOfSale/Pos.Ui/Pos/Icons/employees.png differ diff --git a/PointOfSale/Pos.Ui/Pos/Icons/exchange.png b/PointOfSale/Pos.Ui/Pos/Icons/exchange.png new file mode 100644 index 0000000..64973d5 Binary files /dev/null and b/PointOfSale/Pos.Ui/Pos/Icons/exchange.png differ diff --git a/PointOfSale/Pos.Ui/Pos/Icons/pos.png b/PointOfSale/Pos.Ui/Pos/Icons/pos.png new file mode 100644 index 0000000..4868792 Binary files /dev/null and b/PointOfSale/Pos.Ui/Pos/Icons/pos.png differ diff --git a/PointOfSale/Pos.Ui/Pos/Icons/product.png b/PointOfSale/Pos.Ui/Pos/Icons/product.png new file mode 100644 index 0000000..0cee4d6 Binary files /dev/null and b/PointOfSale/Pos.Ui/Pos/Icons/product.png differ diff --git a/PointOfSale/Pos.Ui/Pos/Icons/receipt.png b/PointOfSale/Pos.Ui/Pos/Icons/receipt.png new file mode 100644 index 0000000..1403a8a Binary files /dev/null and b/PointOfSale/Pos.Ui/Pos/Icons/receipt.png differ diff --git a/PointOfSale/Pos.Ui/Pos/Icons/settings.png b/PointOfSale/Pos.Ui/Pos/Icons/settings.png new file mode 100644 index 0000000..f31d364 Binary files /dev/null and b/PointOfSale/Pos.Ui/Pos/Icons/settings.png differ diff --git a/PointOfSale/Pos.Ui/Pos/Json/BodyModel.cs b/PointOfSale/Pos.Ui/Pos/Json/BodyModel.cs new file mode 100644 index 0000000..759e8fe --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Json/BodyModel.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pos.Json +{ + public class BodyModel + { + public ProductModel[] products { get; set; } + public float totalPrice { get; set; } + public float totalVat { get; set; } + public int receiptNumber { get; set; } + public string receiptTime { get; set; } + public string staff { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Json/FooterModel.cs b/PointOfSale/Pos.Ui/Pos/Json/FooterModel.cs new file mode 100644 index 0000000..f17fc20 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Json/FooterModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pos.Json +{ + public class FooterModel + { + public string value { get; set; } + public PrintStylesModel printStyles { get; set; } + public int textAlignment { get; set; } + public int feedingLines { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Json/HeaderModel.cs b/PointOfSale/Pos.Ui/Pos/Json/HeaderModel.cs new file mode 100644 index 0000000..7b63d15 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Json/HeaderModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pos.Json +{ + public class HeaderModel + { + public string value { get; set; } + public PrintStylesModel printStyles { get; set; } + public int textAlignment { get; set; } + public int feedingLines { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Json/PosReceipt.cs b/PointOfSale/Pos.Ui/Pos/Json/PosReceipt.cs new file mode 100644 index 0000000..2b400c3 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Json/PosReceipt.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using EpsonPrinter.Model; + +namespace Pos.Json +{ + public class PosReceipt + { + public string logoBase64 { get; set; } + public HeaderModel[] header { get; set; } + public BodyModel bodyModel { get; set; } = new(); + public FooterModel[] footer { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Json/PrintStylesModel.cs b/PointOfSale/Pos.Ui/Pos/Json/PrintStylesModel.cs new file mode 100644 index 0000000..f21910f --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Json/PrintStylesModel.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pos.Json +{ + public class PrintStylesModel + { + public bool fontB { get; set; } + public bool bold { get; set; } + public bool doubleHeight { get; set; } + public bool doubleWidth { get; set; } + public bool underline { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Json/ProductModel.cs b/PointOfSale/Pos.Ui/Pos/Json/ProductModel.cs new file mode 100644 index 0000000..32a4ae8 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Json/ProductModel.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pos.Json +{ + public class ProductModel + { + public string product { get; set; } + public string noOfProduct { get; set; } + public float price { get; set; } + public float totalPrice { get; set; } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/MainWindow.xaml b/PointOfSale/Pos.Ui/Pos/MainWindow.xaml new file mode 100644 index 0000000..7db951b --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/MainWindow.xaml @@ -0,0 +1,16 @@ + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/SaleOfDay.xaml.cs b/PointOfSale/Pos.Ui/Pos/SaleOfDay.xaml.cs new file mode 100644 index 0000000..b40f5c5 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/SaleOfDay.xaml.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using EpsonPrinter.Model; +using Pos.Models; +using Pos.Service; + +namespace Pos +{ + /// + /// Interaction logic for SaleOfDay.xaml + /// + public partial class SaleOfDay : Window + { + private bool _initialLoad = true; + private TotalSaleDetail totalSaleDetail; + public SaleOfDay() + { + InitializeComponent(); + SaleService saleService = new SaleService(); + totalSaleDetail = saleService.TotalSale(DateTime.Now); + TotalSale.Content = $"Dagens salg: kr. {totalSaleDetail.TotalSale:0.00}"; + StartDatePicker.SelectedDate = DateTime.Now; + GridSaleDetail.ItemsSource = totalSaleDetail.TotalSaleCategories; + _initialLoad = false; + } + + private void StartDatePicker_SelectedDateChanged(object sender, SelectionChangedEventArgs e) + { + if (_initialLoad) + return; + + _initialLoad = false; + DateTime selectedDate = DateTime.Parse(sender.ToString()); + SaleService saleService = new SaleService(); + totalSaleDetail = saleService.TotalSale(selectedDate); + TotalSale.Content = $"Dagens salg: kr. {totalSaleDetail.TotalSale:0.00}"; + GridSaleDetail.ItemsSource = totalSaleDetail.TotalSaleCategories; + } + + private void PrintSale_OnClick(object sender, RoutedEventArgs e) + { + SaleService saleService = new SaleService(); + saleService.PrintSaleOfDay(StartDatePicker.SelectedDate.Value,totalSaleDetail); + } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/SalesWindow.xaml b/PointOfSale/Pos.Ui/Pos/SalesWindow.xaml new file mode 100644 index 0000000..7a7757e --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/SalesWindow.xaml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Employee/AddEmployeeWindow.xaml.cs b/PointOfSale/Pos.Ui/Pos/Setting/Employee/AddEmployeeWindow.xaml.cs new file mode 100644 index 0000000..43844f4 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Employee/AddEmployeeWindow.xaml.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Database.Repository; + +namespace Pos.Setting +{ + /// + /// Interaction logic for AddEmployeeWindow.xaml + /// + public partial class AddEmployeeWindow : Window + { + public AddEmployeeWindow() + { + InitializeComponent(); + } + + private void btnAdd_Click(object sender, RoutedEventArgs e) + { + EmployeeRepository employeeRepository = new EmployeeRepository(); + employeeRepository.Add(txtStaff.Text); + this.Close(); + } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Employee/DeleteEmployeeWindow.xaml b/PointOfSale/Pos.Ui/Pos/Setting/Employee/DeleteEmployeeWindow.xaml new file mode 100644 index 0000000..ff27339 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Employee/DeleteEmployeeWindow.xaml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Employee/DeleteEmployeeWindow.xaml.cs b/PointOfSale/Pos.Ui/Pos/Setting/Employee/DeleteEmployeeWindow.xaml.cs new file mode 100644 index 0000000..2609278 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Employee/DeleteEmployeeWindow.xaml.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Database.Repository; + +namespace Pos.Setting +{ + /// + /// Interaction logic for DeleteEmployeeWindow.xaml + /// + public partial class DeleteEmployeeWindow : Window + { + private int _employeeId; + + public DeleteEmployeeWindow(int employeeId, string staffName) + { + InitializeComponent(); + _employeeId = employeeId; + txtDelEmployee.Text = staffName; + } + + private void btnYes_Click(object sender, RoutedEventArgs e) + { + EmployeeRepository employeeRepository = new EmployeeRepository(); + employeeRepository.Delete(_employeeId); + this.Close(); + } + + private void btnNo_Click(object sender, RoutedEventArgs e) + { + this.Close(); + } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Employee/EditEmployeeWindow.xaml b/PointOfSale/Pos.Ui/Pos/Setting/Employee/EditEmployeeWindow.xaml new file mode 100644 index 0000000..c4325bd --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Employee/EditEmployeeWindow.xaml @@ -0,0 +1,13 @@ + + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Employee/EditEmployeeWindow.xaml.cs b/PointOfSale/Pos.Ui/Pos/Setting/Employee/EditEmployeeWindow.xaml.cs new file mode 100644 index 0000000..ecfbfa0 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Employee/EditEmployeeWindow.xaml.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Database.Repository; + +namespace Pos.Setting +{ + /// + /// Interaction logic for EditEmployeeWindow.xaml + /// + public partial class EditEmployeeWindow : Window + { + private int _employeeId; + + public EditEmployeeWindow(int employeeId, string staffName) + { + _employeeId = employeeId; + InitializeComponent(); + txtEmployee.Text = staffName; + } + + private void btnEdit_Click(object sender, RoutedEventArgs e) + { + EmployeeRepository employeeRepository = new EmployeeRepository(); + employeeRepository.Edit(_employeeId,txtEmployee.Text); + this.Close(); + } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Employee/EmployeeWindow.xaml b/PointOfSale/Pos.Ui/Pos/Setting/Employee/EmployeeWindow.xaml new file mode 100644 index 0000000..b53b762 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Employee/EmployeeWindow.xaml @@ -0,0 +1,22 @@ + + + + + + + + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Product/AddProductWindow.xaml.cs b/PointOfSale/Pos.Ui/Pos/Setting/Product/AddProductWindow.xaml.cs new file mode 100644 index 0000000..b75fbe9 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Product/AddProductWindow.xaml.cs @@ -0,0 +1,45 @@ +using Database.Repository; +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; + +namespace Pos.Setting.ProductGroup +{ + /// + /// Interaction logic for AddProductWindow.xaml + /// + public partial class AddProductWindow : Window + { + public AddProductWindow() + { + InitializeComponent(); + LoadData(0); + } + + private void LoadData(int selectedIndex) + { + ProductGroupRepository productGroupRepository = new ProductGroupRepository(); + List products = productGroupRepository.GetAll(); + foreach (Database.Models.ProductGroupEntity productGroup in products) + { + ComboBoxItem comboBoxItem = new ComboBoxItem(); + comboBoxItem.Tag = productGroup.Id; + comboBoxItem.Content = productGroup.Name; + cmbProductGroup.Items.Add(comboBoxItem); + } + + cmbProductGroup.SelectedIndex = selectedIndex; + } + + private void btnAdd_Click(object sender, RoutedEventArgs e) + { + ComboBoxItem item = (ComboBoxItem) cmbProductGroup.SelectedItem; + string name = txtProduct.Text; + int id = Convert.ToInt32(item.Tag); + ProductRepository productRepository = new ProductRepository(); + productRepository.Add(name,id); + this.Close(); + } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Product/ArchiveProductWindow.xaml b/PointOfSale/Pos.Ui/Pos/Setting/Product/ArchiveProductWindow.xaml new file mode 100644 index 0000000..35b6524 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Product/ArchiveProductWindow.xaml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Product/ArchiveProductWindow.xaml.cs b/PointOfSale/Pos.Ui/Pos/Setting/Product/ArchiveProductWindow.xaml.cs new file mode 100644 index 0000000..b6126de --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Product/ArchiveProductWindow.xaml.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Database.Repository; + +namespace Pos.Setting.Product +{ + /// + /// Interaction logic for ArchiveProductWindow.xaml + /// + public partial class ArchiveProductWindow : Window + { + private int _productId; + public ArchiveProductWindow(int productId) + { + InitializeComponent(); + _productId = productId; + ProductRepository productRepository = new ProductRepository(); + Database.Models.ProductEntity product = productRepository.GetById(_productId); + txtDelProduct.Text = product.Name; + } + + private void btnYes_Click(object sender, RoutedEventArgs e) + { + ProductRepository productRepository = new ProductRepository(); + productRepository.Archive(_productId); + this.Close(); + } + + private void btnNo_Click(object sender, RoutedEventArgs e) + { + this.Close(); + } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Product/ChangeProductOrderWindow.xaml b/PointOfSale/Pos.Ui/Pos/Setting/Product/ChangeProductOrderWindow.xaml new file mode 100644 index 0000000..ddcb601 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Product/ChangeProductOrderWindow.xaml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Product/ChangeProductOrderWindow.xaml.cs b/PointOfSale/Pos.Ui/Pos/Setting/Product/ChangeProductOrderWindow.xaml.cs new file mode 100644 index 0000000..4b378f7 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Product/ChangeProductOrderWindow.xaml.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Database.Repository; + +namespace Pos.Setting.Product +{ + /// + /// Interaction logic for ChangeProductOrderWindow.xaml + /// + public partial class ChangeProductOrderWindow : Window + { + ObservableCollection _productList = new ObservableCollection(); + private int _productGroupId; + public ChangeProductOrderWindow(int productGroupId) + { + InitializeComponent(); + _productGroupId = productGroupId; + LoadData(); + } + + private void LoadData() + { + ProductRepository productRepository = new ProductRepository(); + List products = productRepository.GetByProductGroup(_productGroupId); + foreach (Database.Models.ProductEntity product in products) + { + _productList.Add(product); + } + lstProduct.Items.Clear(); + lstProduct.DisplayMemberPath = "Name"; + lstProduct.ItemsSource = _productList; + + Style itemContainerStyle = new Style(typeof(ListBoxItem)); + itemContainerStyle.Setters.Add(new Setter(ListBoxItem.AllowDropProperty, true)); + itemContainerStyle.Setters.Add(new EventSetter(ListBoxItem.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(s_PreviewMouseLeftButtonDown))); + itemContainerStyle.Setters.Add(new EventSetter(ListBoxItem.DropEvent, new DragEventHandler(lstCategory_Drop))); + lstProduct.ItemContainerStyle = itemContainerStyle; + } + + void s_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + + if (sender is ListBoxItem) + { + ListBoxItem draggedItem = sender as ListBoxItem; + DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move); + draggedItem.IsSelected = true; + } + } + + void lstCategory_Drop(object sender, DragEventArgs e) + { + Database.Models.ProductEntity droppedData = e.Data.GetData(typeof(Database.Models.ProductEntity)) as Database.Models.ProductEntity; + Database.Models.ProductEntity target = ((ListBoxItem)(sender)).DataContext as Database.Models.ProductEntity; + + int removedIdx = lstProduct.Items.IndexOf(droppedData); + int targetIdx = lstProduct.Items.IndexOf(target); + + if (removedIdx < targetIdx) + { + _productList.Insert(targetIdx + 1, droppedData); + _productList.RemoveAt(removedIdx); + } + else + { + int remIdx = removedIdx + 1; + if (_productList.Count + 1 > remIdx) + { + _productList.Insert(targetIdx, droppedData); + _productList.RemoveAt(remIdx); + } + } + } + + private void btnSave_Click(object sender, RoutedEventArgs e) + { + ProductRepository productRepository = new ProductRepository(); + for (int i = 0; i < _productList.Count; i++) + { + Database.Models.ProductEntity product = _productList[i]; + productRepository.SetIndex(product.Id, i); + } + this.Close(); + } + + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Product/EditProductWindow.xaml b/PointOfSale/Pos.Ui/Pos/Setting/Product/EditProductWindow.xaml new file mode 100644 index 0000000..4292595 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Product/EditProductWindow.xaml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Product/EditProductWindow.xaml.cs b/PointOfSale/Pos.Ui/Pos/Setting/Product/EditProductWindow.xaml.cs new file mode 100644 index 0000000..d375fe4 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Product/EditProductWindow.xaml.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Database.Repository; + +namespace Pos.Setting.Product +{ + /// + /// Interaction logic for EditProductWindow.xaml + /// + public partial class EditProductWindow : Window + { + private int _productId; + private int _selectedProductGroupId; + private Database.Models.ProductEntity _product; + + public EditProductWindow(int productId, int selectedProductGroupId) + { + InitializeComponent(); + _productId = productId; + _selectedProductGroupId = selectedProductGroupId; + Init(); + } + + private void Init() + { + ProductGroupRepository categoryRepository = new ProductGroupRepository(); + List productGroups = categoryRepository.GetAll(); + foreach (Database.Models.ProductGroupEntity productGroup in productGroups) + { + + ComboBoxItem comboBoxItem = new ComboBoxItem(); + comboBoxItem.Tag = productGroup.Id; + comboBoxItem.Content = productGroup.Name; + cmbProductGroup.Items.Add(comboBoxItem); + } + cmbProductGroup.SelectedIndex = _selectedProductGroupId; + + ProductRepository productRepository = new ProductRepository(); + _product = productRepository.GetById(_productId); + txtProduct.Text = _product.Name; + } + + private void btnEdit_Click(object sender, RoutedEventArgs e) + { + ComboBoxItem item = (ComboBoxItem)cmbProductGroup.SelectedItem; + ProductRepository productRepository = new ProductRepository(); + productRepository.Update(_product.Id,(int)item.Tag,txtProduct.Text); + this.Close(); + } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Setting/Product/ProductWindow.xaml b/PointOfSale/Pos.Ui/Pos/Setting/Product/ProductWindow.xaml new file mode 100644 index 0000000..aa50b69 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/Product/ProductWindow.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/AddProductGroupWindow.xaml.cs b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/AddProductGroupWindow.xaml.cs new file mode 100644 index 0000000..4ddb1ab --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/AddProductGroupWindow.xaml.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Database.Repository; + +namespace Pos.Setting.Category +{ + /// + /// Interaction logic for AddProductGroupWindow.xaml + /// + public partial class AddProductGroupWindow : Window + { + public AddProductGroupWindow() + { + InitializeComponent(); + } + + private void btnAdd_Click(object sender, RoutedEventArgs e) + { + ProductGroupRepository categoryRepository = new ProductGroupRepository(); + categoryRepository.Add(txtProductGroup.Text); + this.Close(); + } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ArchiveProductGroupWindow.xaml b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ArchiveProductGroupWindow.xaml new file mode 100644 index 0000000..827a11e --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ArchiveProductGroupWindow.xaml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ArchiveProductGroupWindow.xaml.cs b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ArchiveProductGroupWindow.xaml.cs new file mode 100644 index 0000000..3b79849 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ArchiveProductGroupWindow.xaml.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Database.Repository; + +namespace Pos.Setting.Category +{ + /// + /// Interaction logic for ArchiveProductGroupWindow.xaml + /// + public partial class ArchiveProductGroupWindow : Window + { + private readonly int _id; + public ArchiveProductGroupWindow(string name, int id) + { + InitializeComponent(); + _id = id; + txtDelProductGroup.Text = name; + + } + + private void btnNo_Click(object sender, RoutedEventArgs e) + { + this.Close(); + } + + private void btnYes_Click(object sender, RoutedEventArgs e) + { + ProductGroupRepository categoryRepository = new ProductGroupRepository(); + categoryRepository.Archive(_id); + this.Close(); + } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ChangeProductGroupOrderWindow.xaml b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ChangeProductGroupOrderWindow.xaml new file mode 100644 index 0000000..d8365ab --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ChangeProductGroupOrderWindow.xaml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ChangeProductGroupOrderWindow.xaml.cs b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ChangeProductGroupOrderWindow.xaml.cs new file mode 100644 index 0000000..8c1d84c --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ChangeProductGroupOrderWindow.xaml.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Database.Repository; + +namespace Pos.Setting.Category +{ + /// + /// Interaction logic for ChangeProductGroupOrderWindow.xaml + /// + public partial class ChangeProductGroupOrderWindow : Window + { + ObservableCollection _prodGroupList = new ObservableCollection(); + public ChangeProductGroupOrderWindow() + { + InitializeComponent(); + LoadData(); + } + + private void LoadData() + { + ProductGroupRepository categoryRepository = new ProductGroupRepository(); + List categories = categoryRepository.GetAll(); + foreach (Database.Models.ProductGroupEntity category in categories) + { + _prodGroupList.Add(category); + } + lstCategory.Items.Clear(); + lstCategory.DisplayMemberPath = "Name"; + lstCategory.ItemsSource = _prodGroupList; + + Style itemContainerStyle = new Style(typeof(ListBoxItem)); + itemContainerStyle.Setters.Add(new Setter(ListBoxItem.AllowDropProperty, true)); + itemContainerStyle.Setters.Add(new EventSetter(ListBoxItem.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(s_PreviewMouseLeftButtonDown))); + itemContainerStyle.Setters.Add(new EventSetter(ListBoxItem.DropEvent, new DragEventHandler(lstCategory_Drop))); + lstCategory.ItemContainerStyle = itemContainerStyle; + } + + void s_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + + if (sender is ListBoxItem) + { + ListBoxItem draggedItem = sender as ListBoxItem; + DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move); + draggedItem.IsSelected = true; + } + } + + void lstCategory_Drop(object sender, DragEventArgs e) + { + Database.Models.ProductGroupEntity droppedData = e.Data.GetData(typeof(Database.Models.ProductGroupEntity)) as Database.Models.ProductGroupEntity; + Database.Models.ProductGroupEntity target = ((ListBoxItem)(sender)).DataContext as Database.Models.ProductGroupEntity; + + int removedIdx = lstCategory.Items.IndexOf(droppedData); + int targetIdx = lstCategory.Items.IndexOf(target); + + if (removedIdx < targetIdx) + { + _prodGroupList.Insert(targetIdx + 1, droppedData); + _prodGroupList.RemoveAt(removedIdx); + } + else + { + int remIdx = removedIdx + 1; + if (_prodGroupList.Count + 1 > remIdx) + { + _prodGroupList.Insert(targetIdx, droppedData); + _prodGroupList.RemoveAt(remIdx); + } + } + } + + private void btnSave_Click(object sender, RoutedEventArgs e) + { + ProductGroupRepository categoryRepository = new ProductGroupRepository(); + for (int i = 0; i < _prodGroupList.Count; i++) + { + Database.Models.ProductGroupEntity productGroup = _prodGroupList[i]; + categoryRepository.SetIndex(productGroup.Id,i); + } + this.Close(); + } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/EditProductGroupWindow.xaml b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/EditProductGroupWindow.xaml new file mode 100644 index 0000000..2554633 --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/EditProductGroupWindow.xaml @@ -0,0 +1,13 @@ + + + + + + diff --git a/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/EditProductGroupWindow.xaml.cs b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/EditProductGroupWindow.xaml.cs new file mode 100644 index 0000000..4b02f8c --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/EditProductGroupWindow.xaml.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using Database.Repository; + +namespace Pos.Setting.Category +{ + /// + /// Interaction logic for EditProductGroupWindow.xaml + /// + public partial class EditProductGroupWindow : Window + { + private readonly int _id; + + public EditProductGroupWindow(string name, int id) + { + InitializeComponent(); + _id = id; + txtProductGroup.Text = name; + } + + private void btnEdit_Click(object sender, RoutedEventArgs e) + { + ProductGroupRepository categoryRepository = new ProductGroupRepository(); + categoryRepository.Edit(txtProductGroup.Text,_id); + this.Close(); + } + } +} diff --git a/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ProductGroupWindow.xaml b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ProductGroupWindow.xaml new file mode 100644 index 0000000..3ef5c0e --- /dev/null +++ b/PointOfSale/Pos.Ui/Pos/Setting/ProductGroup/ProductGroupWindow.xaml @@ -0,0 +1,23 @@ + + + + + + + + + +