diff --git a/android/app/build.gradle b/android/app/build.gradle index 6dd7daa..b57e3a6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -177,6 +177,8 @@ android { } dependencies { + compile project(':react-native-nfc-manager') + compile project(':react-native-key-event') compile project(':react-native-localization') compile project(':react-native-background-job') compile project(':react-native-bluetooth-status') diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8babdb9..58ce102 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -9,7 +9,8 @@ - + + + android:screenOrientation="landscape" + android:launchMode="singleTask"> + + + + + + + + + + + + + diff --git a/android/app/src/main/java/com/tailpos/MainActivity.java b/android/app/src/main/java/com/tailpos/MainActivity.java index 3489891..c2d3831 100644 --- a/android/app/src/main/java/com/tailpos/MainActivity.java +++ b/android/app/src/main/java/com/tailpos/MainActivity.java @@ -1,7 +1,8 @@ package com.tailpos; import android.os.Bundle; - +import android.view.KeyEvent; // <--- import +import net.kangyufei.KeyEventModule; // <--- import import org.devio.rn.splashscreen.SplashScreen; import com.facebook.react.ReactActivity; @@ -22,4 +23,20 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } + @Override // <--- Add this method if you want to react to keyDown + public boolean onKeyDown(int keyCode, KeyEvent event) { + + KeyEventModule.getInstance().onKeyDownEvent(keyCode, event); + super.onKeyDown(keyCode, event); + return true; + } + + @Override // <--- Add this method if you want to react to keyUp + public boolean onKeyUp(int keyCode, KeyEvent event) { + KeyEventModule.getInstance().onKeyUpEvent(keyCode, event); + + super.onKeyUp(keyCode, event); + return true; + } + } diff --git a/android/app/src/main/java/com/tailpos/MainApplication.java b/android/app/src/main/java/com/tailpos/MainApplication.java index 9e07ab7..0826b6c 100644 --- a/android/app/src/main/java/com/tailpos/MainApplication.java +++ b/android/app/src/main/java/com/tailpos/MainApplication.java @@ -4,6 +4,8 @@ import com.solinor.bluetoothstatus.RNBluetoothManagerPackage; import com.facebook.react.ReactApplication; +import community.revteltech.nfc.NfcManagerPackage; +import net.kangyufei.KeyEventPackage; import com.babisoft.ReactNativeLocalization.ReactNativeLocalizationPackage; import com.pilloxa.backgroundjob.BackgroundJobPackage; import com.learnium.RNDeviceInfo.RNDeviceInfo; @@ -37,6 +39,8 @@ protected List getPackages() { return Arrays.asList( new RNBluetoothManagerPackage(), new MainReactPackage(), + new NfcManagerPackage(), + new KeyEventPackage(), new ReactNativeLocalizationPackage(), new BackgroundJobPackage(), new RNDeviceInfo(), diff --git a/android/app/src/main/res/xml/nfc_tech_filter.xml b/android/app/src/main/res/xml/nfc_tech_filter.xml new file mode 100644 index 0000000..14c651f --- /dev/null +++ b/android/app/src/main/res/xml/nfc_tech_filter.xml @@ -0,0 +1,29 @@ + + + android.nfc.tech.IsoDep + + + android.nfc.tech.NfcA + + + android.nfc.tech.NfcB + + + android.nfc.tech.NfcF + + + android.nfc.tech.NfcV + + + android.nfc.tech.Ndef + + + android.nfc.tech.NdefFormatable + + + android.nfc.tech.MifareClassic + + + android.nfc.tech.MifareUltralight + + \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle index 8900f48..d465898 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,4 +1,8 @@ rootProject.name = 'TailPOS' +include ':react-native-nfc-manager' +project(':react-native-nfc-manager').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-nfc-manager/android') +include ':react-native-key-event' +project(':react-native-key-event').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-key-event/android') include ':react-native-localization' project(':react-native-localization').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-localization/android') include ':react-native-background-job' diff --git a/android/src/main/res/xml/nfc_tech_filter.xml b/android/src/main/res/xml/nfc_tech_filter.xml new file mode 100644 index 0000000..14c651f --- /dev/null +++ b/android/src/main/res/xml/nfc_tech_filter.xml @@ -0,0 +1,29 @@ + + + android.nfc.tech.IsoDep + + + android.nfc.tech.NfcA + + + android.nfc.tech.NfcB + + + android.nfc.tech.NfcF + + + android.nfc.tech.NfcV + + + android.nfc.tech.Ndef + + + android.nfc.tech.NdefFormatable + + + android.nfc.tech.MifareClassic + + + android.nfc.tech.MifareUltralight + + \ No newline at end of file diff --git a/changes/nfc-manager-build.gradle b/changes/nfc-manager-build.gradle new file mode 100644 index 0000000..678946f --- /dev/null +++ b/changes/nfc-manager-build.gradle @@ -0,0 +1,42 @@ +def safeExtGet(prop, fallback) { + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback +} + +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:1.5.0' + } +} +apply plugin: 'com.android.library' + +android { + compileSdkVersion safeExtGet('compileSdkVersion', 27) + buildToolsVersion safeExtGet('buildToolsVersion', '27.0.3') + + defaultConfig { + minSdkVersion safeExtGet('minSdkVersion', 16) + //noinspection OldTargetApi + targetSdkVersion safeExtGet('targetSdkVersion', 27) + } + lintOptions { + abortOnError false + } +} + +repositories { + mavenCentral() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$rootDir/../node_modules/react-native/android" + } +} + + +dependencies { + compile 'com.facebook.react:react-native:+' + +} diff --git a/ios/TailPOS.xcodeproj/project.pbxproj b/ios/TailPOS.xcodeproj/project.pbxproj index 71ffce9..d83f712 100644 --- a/ios/TailPOS.xcodeproj/project.pbxproj +++ b/ios/TailPOS.xcodeproj/project.pbxproj @@ -66,6 +66,7 @@ 43FFBF74D0094DD8866DD727 /* libRNBluetoothManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 114C3F4566DC4AAFBF009FFF /* libRNBluetoothManager.a */; }; 51F0F7BBE5C142D4B8521808 /* libRNBackgroundJob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 030627CBACC14D929C0207BD /* libRNBackgroundJob.a */; }; 520D4F11F21A4CEEB05724EB /* libReactNativeLocalization.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A5548395195745ED8AFFDEBB /* libReactNativeLocalization.a */; }; + 6BEB5BDCB20043EA80E560C6 /* libNfcManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D806BF8811F4260B10899D2 /* libNfcManager.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -413,6 +414,8 @@ 030627CBACC14D929C0207BD /* libRNBackgroundJob.a */ = {isa = PBXFileReference; name = "libRNBackgroundJob.a"; path = "libRNBackgroundJob.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; D5BF8BC30E324943AB843FEB /* ReactNativeLocalization.xcodeproj */ = {isa = PBXFileReference; name = "ReactNativeLocalization.xcodeproj"; path = "../node_modules/react-native-localization/ReactNativeLocalization.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; A5548395195745ED8AFFDEBB /* libReactNativeLocalization.a */ = {isa = PBXFileReference; name = "libReactNativeLocalization.a"; path = "libReactNativeLocalization.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; + 2C27E3CBAD07414F89BF7DAE /* NfcManager.xcodeproj */ = {isa = PBXFileReference; name = "NfcManager.xcodeproj"; path = "../node_modules/react-native-nfc-manager/ios/NfcManager.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; + 2D806BF8811F4260B10899D2 /* libNfcManager.a */ = {isa = PBXFileReference; name = "libNfcManager.a"; path = "libNfcManager.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -455,6 +458,7 @@ 43FFBF74D0094DD8866DD727 /* libRNBluetoothManager.a in Frameworks */, 51F0F7BBE5C142D4B8521808 /* libRNBackgroundJob.a in Frameworks */, 520D4F11F21A4CEEB05724EB /* libReactNativeLocalization.a in Frameworks */, + 6BEB5BDCB20043EA80E560C6 /* libNfcManager.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -657,6 +661,7 @@ 0FC20227933C497CB2330EEF /* RNBluetoothManager.xcodeproj */, 2140AF7DBD7F486D8BC7E6D1 /* RNBackgroundJob.xcodeproj */, D5BF8BC30E324943AB843FEB /* ReactNativeLocalization.xcodeproj */, + 2C27E3CBAD07414F89BF7DAE /* NfcManager.xcodeproj */, ); name = Libraries; sourceTree = ""; @@ -1320,6 +1325,7 @@ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TailPOS.app/TailPOS"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1336,6 +1342,7 @@ "$(SRCROOT)/../node_modules/react-native-bluetooth-status/ios", "$(SRCROOT)/../node_modules/react-native-background-job/ios", "$(SRCROOT)/../node_modules/react-native-localization", + "$(SRCROOT)/../node_modules/react-native-nfc-manager/ios", ); }; name = Debug; @@ -1356,6 +1363,7 @@ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TailPOS.app/TailPOS"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1372,6 +1380,7 @@ "$(SRCROOT)/../node_modules/react-native-bluetooth-status/ios", "$(SRCROOT)/../node_modules/react-native-background-job/ios", "$(SRCROOT)/../node_modules/react-native-localization", + "$(SRCROOT)/../node_modules/react-native-nfc-manager/ios", ); }; name = Release; @@ -1406,6 +1415,7 @@ "$(SRCROOT)/../node_modules/react-native-bluetooth-status/ios", "$(SRCROOT)/../node_modules/react-native-background-job/ios", "$(SRCROOT)/../node_modules/react-native-localization", + "$(SRCROOT)/../node_modules/react-native-nfc-manager/ios", ); }; name = Debug; @@ -1439,6 +1449,7 @@ "$(SRCROOT)/../node_modules/react-native-bluetooth-status/ios", "$(SRCROOT)/../node_modules/react-native-background-job/ios", "$(SRCROOT)/../node_modules/react-native-localization", + "$(SRCROOT)/../node_modules/react-native-nfc-manager/ios", ); }; name = Release; @@ -1468,6 +1479,7 @@ TVOS_DEPLOYMENT_TARGET = 9.2; LIBRARY_SEARCH_PATHS = ( "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1484,6 +1496,7 @@ "$(SRCROOT)/../node_modules/react-native-bluetooth-status/ios", "$(SRCROOT)/../node_modules/react-native-background-job/ios", "$(SRCROOT)/../node_modules/react-native-localization", + "$(SRCROOT)/../node_modules/react-native-nfc-manager/ios", ); }; name = Debug; @@ -1513,6 +1526,7 @@ TVOS_DEPLOYMENT_TARGET = 9.2; LIBRARY_SEARCH_PATHS = ( "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1529,6 +1543,7 @@ "$(SRCROOT)/../node_modules/react-native-bluetooth-status/ios", "$(SRCROOT)/../node_modules/react-native-background-job/ios", "$(SRCROOT)/../node_modules/react-native-localization", + "$(SRCROOT)/../node_modules/react-native-nfc-manager/ios", ); }; name = Release; @@ -1557,6 +1572,7 @@ TVOS_DEPLOYMENT_TARGET = 10.1; LIBRARY_SEARCH_PATHS = ( "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1573,6 +1589,7 @@ "$(SRCROOT)/../node_modules/react-native-bluetooth-status/ios", "$(SRCROOT)/../node_modules/react-native-background-job/ios", "$(SRCROOT)/../node_modules/react-native-localization", + "$(SRCROOT)/../node_modules/react-native-nfc-manager/ios", ); }; name = Debug; @@ -1601,6 +1618,7 @@ TVOS_DEPLOYMENT_TARGET = 10.1; LIBRARY_SEARCH_PATHS = ( "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -1617,6 +1635,7 @@ "$(SRCROOT)/../node_modules/react-native-bluetooth-status/ios", "$(SRCROOT)/../node_modules/react-native-background-job/ios", "$(SRCROOT)/../node_modules/react-native-localization", + "$(SRCROOT)/../node_modules/react-native-nfc-manager/ios", ); }; name = Release; diff --git a/package-lock.json b/package-lock.json index 4027ade..5c93029 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4329,8 +4329,7 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true + "bundled": true }, "aproba": { "version": "1.1.1", @@ -4395,7 +4394,6 @@ "boom": { "version": "2.10.1", "bundled": true, - "optional": true, "requires": { "hoek": "2.x.x" } @@ -4538,13 +4536,11 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, - "optional": true + "bundled": true }, "fstream": { "version": "1.0.11", "bundled": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -4595,7 +4591,6 @@ "glob": { "version": "7.1.2", "bundled": true, - "optional": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4607,8 +4602,7 @@ }, "graceful-fs": { "version": "4.1.11", - "bundled": true, - "optional": true + "bundled": true }, "har-schema": { "version": "1.0.5", @@ -4642,8 +4636,7 @@ }, "hoek": { "version": "2.16.3", - "bundled": true, - "optional": true + "bundled": true }, "http-signature": { "version": "1.1.1", @@ -4658,7 +4651,6 @@ "inflight": { "version": "1.0.6", "bundled": true, - "optional": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -4666,8 +4658,7 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.4", @@ -4767,7 +4758,6 @@ "minimatch": { "version": "3.0.4", "bundled": true, - "optional": true, "requires": { "brace-expansion": "1.1.7" } @@ -4780,7 +4770,6 @@ "mkdirp": { "version": "0.5.1", "bundled": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -4813,8 +4802,8 @@ "bundled": true, "optional": true, "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npmlog": { @@ -4846,7 +4835,6 @@ "once": { "version": "1.4.0", "bundled": true, - "optional": true, "requires": { "wrappy": "1" } @@ -4866,14 +4854,13 @@ "bundled": true, "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "performance-now": { "version": "0.2.0", @@ -4959,7 +4946,6 @@ "rimraf": { "version": "2.6.1", "bundled": true, - "optional": true, "requires": { "glob": "^7.0.5" } @@ -5041,7 +5027,6 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5130,8 +5115,7 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true } } }, @@ -10082,6 +10066,11 @@ "resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.0.2.tgz", "integrity": "sha512-5FYNC4kTi/YK86l+r8GQ0xgsSL2tleCQ5Yppu1+ARbnm2qGRmDoJTGSNsWBAWa8FP1ORyhMjxi18IlvSRKaI2g==" }, + "react-native-key-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/react-native-key-event/-/react-native-key-event-1.0.0.tgz", + "integrity": "sha512-bb57EC7q9Y+YUS29Z461neSZlMYKq92x1J1zOd+TdE9Sos3Iqz+c1AwCpng1ob4nR2FUwqI+QAQ3j57rHF5dRQ==" + }, "react-native-keyboard-aware-scroll-view": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/react-native-keyboard-aware-scroll-view/-/react-native-keyboard-aware-scroll-view-0.5.0.tgz", @@ -10156,6 +10145,11 @@ "react-native-animatable": "^1.2.4" } }, + "react-native-nfc-manager": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/react-native-nfc-manager/-/react-native-nfc-manager-1.2.2.tgz", + "integrity": "sha512-mOJD2YDpPaG+JzsjB03c+xDsRHr2Vm5CFRr9rtMRt+c/9jdvn/c2+VXCFA5/PJ1GEXdY/vFFOP96Qv5kAau8Mw==" + }, "react-native-orientation": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/react-native-orientation/-/react-native-orientation-3.1.3.tgz", diff --git a/package.json b/package.json index cdd7fea..026c312 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "prettier": "prettier --trailing-comma all --write \"src/**/*.js\"", "clean": "rm -rf $TMPDIR/react-* && rm -rf node_modules/", "apk": "cd android && ./gradlew assembleRelease && cd ..", - "change": "cp changes/localization-build.gradle node_modules/react-native-localization/android/build.gradle && cp changes/deviceinfo-build.gradle node_modules/react-native-device-info/android/build.gradle && cp changes/camera-build.gradle node_modules/react-native-camera/android/build.gradle && cp changes/maps-build.gradle node_modules/react-native-maps/lib/android/build.gradle && cp changes/RCTBluetoothSerialPackage.java node_modules/react-native-bluetooth-serial/android/src/main/java/com/rusel/RCTBluetoothSerial/" + "change": "cp changes/nfc-manager-build.gradle node_modules/react-native-nfc-manager/android/build.gradle && cp changes/localization-build.gradle node_modules/react-native-localization/android/build.gradle && cp changes/deviceinfo-build.gradle node_modules/react-native-device-info/android/build.gradle && cp changes/camera-build.gradle node_modules/react-native-camera/android/build.gradle && cp changes/maps-build.gradle node_modules/react-native-maps/lib/android/build.gradle && cp changes/RCTBluetoothSerialPackage.java node_modules/react-native-bluetooth-serial/android/src/main/java/com/rusel/RCTBluetoothSerial/" }, "jest": { "preset": "react-native", @@ -87,9 +87,11 @@ "react-native-datepicker": "^1.7.2", "react-native-device-info": "^0.21.5", "react-native-frappe-fetch": "^0.9.1", + "react-native-key-event": "^1.0.0", "react-native-localization": "^2.1.2", "react-native-maps": "^0.21.0", "react-native-modal": "^6.0.0", + "react-native-nfc-manager": "^1.2.2", "react-native-orientation": "^3.1.3", "react-native-searchable-dropdown": "^1.0.4", "react-native-simple-dialogs": "^0.3.1", diff --git a/src/boot/background_job.js b/src/boot/background_job.js new file mode 100644 index 0000000..b6186ed --- /dev/null +++ b/src/boot/background_job.js @@ -0,0 +1,18 @@ +import BackgroundJob from "react-native-background-job"; +import { syncObjectValues } from "../store/PosStore/syncInBackground"; + +export function background_job_initialization(stores2) { + BackgroundJob.cancel({ jobKey: "AutomaticSync" }); + const backgroundJob = { + jobKey: "myJob", + job: () => syncObjectValues("sync", stores2, true), + }; + BackgroundJob.register(backgroundJob); + let backgroundSchedule = { + jobKey: "myJob", + period: 360000, + allowExecutionInForeground: true, + networkType: BackgroundJob.NETWORK_TYPE_UNMETERED, + }; + BackgroundJob.schedule(backgroundSchedule); +} diff --git a/src/boot/index.js b/src/boot/index.js index 9b2d2b9..2f63c75 100644 --- a/src/boot/index.js +++ b/src/boot/index.js @@ -38,7 +38,6 @@ export default function() { } } }); - Promise.all([ favoriteItemPromise, itemsLength, @@ -54,14 +53,33 @@ export default function() { attendantPromise, rolePromise, ]) + .then(() => stores.receiptStore.setDefaultCustomer()) .then(() => { - stores.receiptStore.currentReceipt( - stores.printerStore.companySettings[0].tax, - ); + const { initializeState } = stores.stateStore; + initializeState(); + stores.stateStore.changeCompanyCheckBox( stores.printerStore.companySettings[0].currencyDisable, ); + stores.stateStore.changeOverallTax( + stores.printerStore.companySettings[0].enableOverallTax, + ); + stores.stateStore.changeValue( + "multipleMop", + stores.printerStore.companySettings[0].multipleMop, + "Settings", + ); + stores.stateStore.changeValue( + "hideMenuBar", + stores.printerStore.companySettings[0].hideMenuBar, + "Settings", + ); + stores.stateStore.changeValue( + "allowRoundOff", + stores.printerStore.companySettings[0].allowRoundOff, + "Settings", + ); }); return app(stores); diff --git a/src/boot/setup.js b/src/boot/setup.js index 8c3c2e8..38707d1 100755 --- a/src/boot/setup.js +++ b/src/boot/setup.js @@ -2,29 +2,17 @@ import * as React from "react"; import { Provider } from "mobx-react/native"; import { StyleProvider } from "native-base"; import Orientation from "react-native-orientation"; -import BackgroundJob from "react-native-background-job"; import config from "./configureStore"; -import { syncObjectValues } from "../store/PosStore/syncInBackground"; +import { background_job_initialization } from "./background_job"; import App from "../App"; import getTheme from "../theme/components"; import variables from "../theme/variables/platform"; const stores2 = config(); -BackgroundJob.cancel({ jobKey: "AutomaticSync" }); -const backgroundJob = { - jobKey: "myJob", - job: () => syncObjectValues("sync", stores2, true), -}; -BackgroundJob.register(backgroundJob); -var backgroundSchedule = { - jobKey: "myJob", - period: 360000, - allowExecutionInForeground: true, - networkType: BackgroundJob.NETWORK_TYPE_UNMETERED, -}; -BackgroundJob.schedule(backgroundSchedule); - +const { initializeState } = stores2.stateStore; +initializeState(); +background_job_initialization(stores2); export default function(stores) { return class Setup extends React.Component { constructor(props) { diff --git a/src/container/ListingContainer/index.js b/src/container/ListingContainer/index.js index 2b4fc94..60457c1 100644 --- a/src/container/ListingContainer/index.js +++ b/src/container/ListingContainer/index.js @@ -350,7 +350,6 @@ export default class ListingContainer extends React.Component { JSON.stringify(index.taxesValue), "Listing", ); - if (this.props.stateStore.listing_state[0].itemMaintenanceStatus) { this.props.itemStore.setItem(index); } else { @@ -431,6 +430,7 @@ export default class ListingContainer extends React.Component { name: item.name, sku: item.sku, price: item.price, + tax: item.tax, soldBy: item.soldBy, barcode: item.barcode, description: item.name, @@ -451,6 +451,7 @@ export default class ListingContainer extends React.Component { description: item.name, soldBy: item.soldBy, price: unformat(item.price), + tax: unformat(item.tax), sku: item.sku, syncStatus: false, barcode: item.barcode, @@ -476,6 +477,7 @@ export default class ListingContainer extends React.Component { description: item.name, soldBy: item.soldBy, price: unformat(item.price), + tax: unformat(item.tax), sku: item.sku, barcode: item.barcode, category: item.category, @@ -500,6 +502,7 @@ export default class ListingContainer extends React.Component { soldBy: item.soldBy, price: item.price, sku: item.sku, + tax: item.tax, barcode: item.barcode, colorAndShape: item.colorAndShape, category: item.category, @@ -526,6 +529,7 @@ export default class ListingContainer extends React.Component { name: item.name, soldBy: item.soldBy, price: item.price, + tax: item.tax, sku: item.sku, barcode: item.barcode, colorAndShape: item.colorAndShape, @@ -545,6 +549,7 @@ export default class ListingContainer extends React.Component { name: item.name, soldBy: item.soldBy, price: unformat(item.price), + tax: unformat(item.tax), sku: item.sku, barcode: item.barcode, category: item.category, @@ -570,6 +575,7 @@ export default class ListingContainer extends React.Component { name: item.name, soldBy: item.soldBy, price: unformat(item.price), + tax: unformat(item.tax), sku: item.sku, barcode: item.barcode, category: item.category, @@ -595,6 +601,7 @@ export default class ListingContainer extends React.Component { name: item.name, soldBy: item.soldBy, price: item.price, + tax: item.tax, sku: item.sku, barcode: item.barcode, colorAndShape: item.colorAndShape, @@ -698,6 +705,7 @@ export default class ListingContainer extends React.Component { const itemTab = ( 0 ? this.props.itemStore.queriedRows.slice().sort(sortByName) @@ -738,6 +746,7 @@ export default class ListingContainer extends React.Component { const categoryTab = ( { - let routeName = ""; - if (res.result || res.rowsLength > 0) { - routeName = "Pin"; - } else { - routeName = "Login"; - } - const resetAction = NavigationActions.reset({ - index: 0, - key: null, - actions: [NavigationActions.navigate({ routeName })], - }); - this.props.navigation.dispatch(resetAction); + this.props.attendantStore.getData().then(async res => { + await this.props.receiptStore.currentReceipt( + this.props.printerStore.companySettings[0].tax, + ); + setTimeout(() => { + let routeName = ""; + if (res.result || res.rowsLength > 0) { + routeName = "Pin"; + } else { + routeName = "Login"; + } + + const resetAction = NavigationActions.reset({ + index: 0, + key: null, + actions: [NavigationActions.navigate({ routeName })], + }); + this.props.navigation.dispatch(resetAction); + }, 3000); }); } diff --git a/src/container/LoginContainer/index.js b/src/container/LoginContainer/index.js index 8689c8c..29e05e2 100644 --- a/src/container/LoginContainer/index.js +++ b/src/container/LoginContainer/index.js @@ -81,6 +81,7 @@ export default class LoginContainer extends React.Component { pin_code: this.state.pin, role: "Owner", canLogin: true, + canApprove: true, dateUpdated: Date.now(), syncStatus: false, }); diff --git a/src/container/PaymentContainer/controller.js b/src/container/PaymentContainer/controller.js index e0dee45..1b0ef0b 100644 --- a/src/container/PaymentContainer/controller.js +++ b/src/container/PaymentContainer/controller.js @@ -1,3 +1,8 @@ +import { + nfc_initialization, + // validate_tag_event, + unregister_tag_event, +} from "./nfc_manager_initialization"; export default class PaymentController { constructor(stateStore) { this.stateStore = stateStore; @@ -5,9 +10,32 @@ export default class PaymentController { modalVisibleChange = modalVisible => { this.stateStore.changeValue("modalVisible", modalVisible, "Payment"); }; - onChangePayment = payment => { + onChangePayment = (payment, props) => { this.stateStore.changeValue("selected", payment, "Payment"); + if ( + payment === "Wallet" && + !props.stateStore.settings_state[0].multipleMop + ) { + this.stateStore.setPaymentValue(this.stateStore.amount_due); + nfc_initialization(props, this.stateStore.deviceId); + // validate_tag_event( + // { + // techTypes: [ + // "android.nfc.tech.NfcA", + // "android.nfc.tech.MifareClassic", + // "android.nfc.tech.NdefFormatable", + // ], + // id: "9AF076DF", + // }, + // props, + // this.stateStore.deviceId, + // ); + } else { + this.stateStore.setPaymentValue("0"); + unregister_tag_event(); + } }; + onChangeCustomerName = customerName => { this.stateStore.changeValue("customerName", customerName, "Payment"); }; diff --git a/src/container/PaymentContainer/index.js b/src/container/PaymentContainer/index.js index c2bc236..712c930 100644 --- a/src/container/PaymentContainer/index.js +++ b/src/container/PaymentContainer/index.js @@ -3,21 +3,19 @@ import { Alert } from "react-native"; import { Toast } from "native-base"; import BluetoothSerial from "react-native-bluetooth-serial"; import { BluetoothStatus } from "react-native-bluetooth-status"; -import TinyPOS from "tiny-esc-pos"; -import { formatNumber } from "accounting-js"; import * as EmailValidator from "email-validator"; import { inject, observer } from "mobx-react/native"; +import { unregister_tag_event } from "./nfc_manager_initialization"; import { currentLanguage } from "../../translations/CurrentLanguage"; import PaymentController from "./controller"; import PaymentScreen from "@screens/Payment"; - +import { on_pay } from "./on_pay"; import translation from "../../translations/translation"; import LocalizedStrings from "react-native-localization"; +import { check_customers_pin } from "./nfc_manager_initialization"; let strings = new LocalizedStrings(translation); -const moment = require("moment"); - @inject( "itemStore", "customerStore", @@ -39,6 +37,20 @@ export default class PaymentContainer extends React.Component { } componentWillMount() { + this.props.stateStore.resetScannedNfc(); + this.props.stateStore.set_customers_pin(""); + this.props.stateStore.is_not_customers_pin(); + this.props.stateStore.resetPaymentTypes(); + this.props.stateStore.setMopAmount("0"); + const { stateStore } = this.props; + this.props.stateStore.setBalance( + ( + parseFloat(stateStore.amount_due, 10) - + parseFloat(this.get_payment_total(), 10) + ).toString(), + ); + this.props.stateStore.changeValue("selected", "Cash", "Payment"); + this.props.stateStore.setPaymentValue("0"); if (this.props.customerStore.rows.length > 0) { @@ -124,932 +136,41 @@ export default class PaymentContainer extends React.Component { } onValueChange = text => { + let payment_state_values = this.props.stateStore.payment_state[0].toJSON(); + let value = + payment_state_values.selected === "Wallet" + ? this.props.stateStore.customers_pin_value + : this.props.stateStore.payment_value; + const { setPaymentValue, set_customers_pin } = this.props.stateStore; if (text === "Del") { - const finalValue = this.props.stateStore.payment_value.slice(0, -1); - this.props.stateStore.setPaymentValue(finalValue); + const finalValue = value.slice(0, -1); + payment_state_values.selected === "Wallet" + ? set_customers_pin(finalValue) + : setPaymentValue(finalValue); } else { if (text.length > 1) { - this.props.stateStore.setPaymentValue(text); + payment_state_values.selected === "Wallet" + ? set_customers_pin(text) + : setPaymentValue(text); } else { if (this.props.stateStore.payment_value === "0") { - this.props.stateStore.setPaymentValue(text); + payment_state_values.selected === "Wallet" + ? set_customers_pin(text) + : setPaymentValue(text); } else { - this.props.stateStore.setPaymentValue( - this.props.stateStore.payment_value + text, - ); + payment_state_values.selected === "Wallet" + ? set_customers_pin(value + text) + : setPaymentValue(value + text); } } } }; - setOrderCompleted() { - const { - queueOrigin, - currentTable, - setCurrentTable, - } = this.props.stateStore; - - const url = `${queueOrigin}/api/v1/complete_order`; - const fetchData = { - method: "POST", - body: JSON.stringify({ - id: currentTable, - }), - }; - - fetch(url, fetchData) - .then(res => res.json()) - .then(res => setCurrentTable(-1)); - } - onPay = async () => { - const paymentValue = parseFloat(this.props.stateStore.payment_value); - const amountDue = parseFloat(this.props.stateStore.amount_due); - - if (paymentValue < amountDue) { - Alert.alert( - strings.Alert, - strings.AmountPaidMustBeGreaterThanOrEqualToAmountDue, - ); - } else if (paymentValue >= amountDue) { - let receiptNumber = await this.props.receiptStore.numberOfReceipts(); - let receiptNumberLength = receiptNumber.toString().length; - let finalReceiptNumber = ""; - for ( - let lengthNumber = 0; - lengthNumber < 15 - receiptNumberLength; - lengthNumber += 1 - ) { - finalReceiptNumber = finalReceiptNumber + "0"; - } - finalReceiptNumber = finalReceiptNumber + receiptNumber.toString(); - - const receiptCurrent = this.props.receiptStore.defaultReceipt; - const { deviceId } = this.props.stateStore; - - if (deviceId) { - receiptCurrent.setDeviceId(deviceId); - } - - BluetoothSerial.isConnected().then(res => { - let totalPurchase = 0.0; - Alert.alert( - strings.ReceiptConfirmation, // title - strings.DoYouWantToPrintReceipt, - [ - { - text: strings.No, - style: "cancel", - onPress: () => { - this.setOrderCompleted(); - this.props.shiftStore.defaultShift.addTotalDiscount( - receiptCurrent.discounts, - ); - this.props.shiftStore.defaultShift.addTotalTaxes( - parseFloat(this.props.receiptStore.defaultReceipt.subtotal) * - (parseFloat(receiptCurrent.taxesValue) / 100), - ); - this.props.shiftStore.defaultShift.addNumberOfTransaction(); - - let totalAmountDue = 0.0; - - this.props.receiptStore.defaultReceipt.lines.map(val => { - totalAmountDue = - parseInt(totalAmountDue, 10) + - parseInt(val.price.toFixed(2), 10) * - parseInt(val.qty.toFixed(2), 10); - if (val.category && val.category !== "No Category") { - this.props.shiftStore.defaultShift.categoriesAmounts({ - name: val.category, - total_amount: - parseInt(val.price.toFixed(2), 10) * - parseInt(val.qty.toFixed(2), 10), - }); - } - if (this.props.stateStore.payment_state[0].selected) { - this.props.shiftStore.defaultShift.mopAmounts({ - name: this.props.stateStore.payment_state[0].selected, - total_amount: - parseInt(val.price.toFixed(2), 10) * - parseInt(val.qty.toFixed(2), 10), - }); - } - }); - if ( - this.props.receiptStore.defaultReceipt.orderType !== "None" - ) { - this.props.shiftStore.defaultShift.addOrderType({ - amount: parseFloat(totalAmountDue, 10), - type: this.props.receiptStore.defaultReceipt.orderType, - }); - } - this.props.shiftStore.defaultShift.addTotalSales( - totalAmountDue, - ); - this.props.receiptStore.defaultReceipt.lines.map(val => { - totalPurchase = - parseFloat(totalPurchase, 10) + - parseFloat(val.price, 10) * parseFloat(val.qty, 10); - }); - - receiptCurrent.completed( - this.props.attendantStore.defaultAttendant.user_name, - ); - const { defaultShift } = this.props.shiftStore; - - // If shift started and shift hasn't ended - if (defaultShift.shiftStarted && !defaultShift.shiftEnded) { - // Set the default receipt - const { defaultReceipt } = this.props.receiptStore; - - // set shift - defaultReceipt.setShift(defaultShift._id); - - const { ending_cash } = defaultShift; - - // Set the end cash - defaultShift.setEndCash( - ending_cash + defaultReceipt.netTotal, - ); - } - - this.props.receiptStore.defaultReceipt.changeTaxesAmount( - this.props.receiptStore.defaultReceipt.get_tax_total, - ); - - // this.props.receiptStore.defaultReceipt.clear(); - this.props.paymentStore.add({ - receipt: this.props.receiptStore.defaultReceipt._id.toString(), - date: Date.now(), - paid: parseInt(this.props.stateStore.payment_value, 10), - type: this.props.stateStore.payment_state[0].selected, - dateUpdated: Date.now(), - syncStatus: false, - }); - this.props.receiptStore.add( - this.props.receiptStore.defaultReceipt, - ); - this.props.receiptStore.setPreviousReceipt( - this.props.receiptStore.defaultReceipt, - ); - let discountValueForDisplay = this.props.receiptStore - .defaultReceipt.discounts; - let taxesValueForDisplay = this.props.receiptStore - .defaultReceipt.get_tax_total; - this.props.receiptStore.newReceipt( - this.props.printerStore.companySettings[0].tax, - ); - this.props.receiptStore.setLastScannedBarcode(""); - this.props.receiptStore.unselectReceiptLine(); - this.props.navigation.navigate("Sales", { - cash: this.props.stateStore.payment_value, - change: parseFloat( - parseFloat(this.props.stateStore.payment_value, 10) - - (parseFloat(totalPurchase, 10) - - parseFloat(discountValueForDisplay, 10) + - parseFloat(taxesValueForDisplay, 10)), - 10, - ), - }); - }, - }, - { - text: strings.Yes, - onPress: () => { - this.setOrderCompleted(); - this.props.shiftStore.defaultShift.addTotalDiscount( - receiptCurrent.discounts, - ); - this.props.shiftStore.defaultShift.addTotalTaxes( - parseFloat(this.props.receiptStore.defaultReceipt.subtotal) * - (parseFloat(receiptCurrent.taxesValue) / 100), - ); - this.props.shiftStore.defaultShift.addNumberOfTransaction(); - - // Let me print first - let totalAmountDue = 0.0; - let commission_toto = 0.0; - this.props.receiptStore.defaultReceipt.lines.map(val => { - // const { defaultShift } = this.props.shiftStore; - let ComHolder = JSON.parse(val.commission_details); - ComHolder.map(val2 => { - commission_toto = - commission_toto + parseInt(val2.commission_amount, 10); - }); - // defaultShift.addCommission( - // parseInt(val.commission_amount, 10), - // ); - totalAmountDue = - parseInt(totalAmountDue, 10) + - parseInt(val.price.toFixed(2), 10) * - parseInt(val.qty.toFixed(2), 10); - if (val.category && val.category !== "No Category") { - this.props.shiftStore.defaultShift.categoriesAmounts({ - name: val.category, - total_amount: - parseInt(val.price.toFixed(2), 10) * - parseInt(val.qty.toFixed(2), 10), - }); - } - if (this.props.stateStore.payment_state[0].selected) { - this.props.shiftStore.defaultShift.mopAmounts({ - name: this.props.stateStore.payment_state[0].selected, - total_amount: - parseInt(val.price.toFixed(2), 10) * - parseInt(val.qty.toFixed(2), 10), - }); - } - }); - if ( - this.props.receiptStore.defaultReceipt.orderType !== "None" - ) { - this.props.shiftStore.defaultShift.addOrderType({ - amount: parseFloat(totalAmountDue, 10), - type: this.props.receiptStore.defaultReceipt.orderType, - }); - } - this.props.shiftStore.defaultShift.addTotalSales( - totalAmountDue, - ); - if (res) { - const writePromises = []; - - writePromises.push(BluetoothSerial.write(TinyPOS.init())); - - // Header - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${ - this.props.printerStore.companySettings.length > 0 - ? this.props.printerStore.companySettings[0].name - ? this.props.printerStore.companySettings[0].name.toString() - : "Bai Web and Mobile Lab" - : "Bai Web and Mobile Lab" - }`, - { align: "center", size: "doubleheight" }, - true, - ), - ), - ); - - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${ - this.props.printerStore.companySettings.length > 0 - ? this.props.printerStore.companySettings[0].header.toString() - : "" - }`, - { align: "center", size: "normal" }, - true, - ), - ), - ); - - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - "================================", - { size: "normal" }, - true, - ), - ), - ); - - // Date - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${moment().format("YYYY/MM/D hh:mm:ss SSS")}`, - { size: "normal" }, - true, - ), - ), - ); - - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - "================================", - { size: "normal" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - strings.Cashier + - `${ - this.props.attendantStore.defaultAttendant.user_name - }`, - { align: "left", size: "normal" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - strings.TransactionNo + `${finalReceiptNumber}`, - { align: "left", size: "normal" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - "================================", - { size: "normal" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - "Mode of payment: " + - this.props.stateStore.payment_state[0].selected, - { align: "left", size: "normal" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - "================================", - { size: "normal" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - strings.Purchases, - { align: "center", size: "normal" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - strings.Items + - " " + - strings.Amount + - " ", - { align: "left", size: "normal", weight: "bold" }, - true, - ), - ), - ); - - this.props.receiptStore.defaultReceipt.lines.map(val => { - let finalLines = ""; - - const name = val.item_name; - - if (name.length > 14) { - let quotientValue = name.length / 14; - for ( - let quotient = 0; - quotient < parseInt(quotientValue, 10); - quotient += 1 - ) { - let currentCounter = quotient * 14; - let nameCounter = ""; - for ( - let n = currentCounter; - n < (quotient + 1) * 14; - n += 1 - ) { - nameCounter = nameCounter + name[n]; - } - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${nameCounter}`, - { align: "left", size: "normal" }, - true, - ), - ), - ); - } - if (name.length - parseInt(quotientValue, 10) * 14 > 0) { - let nameCounterOverflow = ""; - for ( - let m = parseInt(quotientValue, 10) * 14; - m < name.length; - m += 1 - ) { - nameCounterOverflow = nameCounterOverflow + name[m]; - } - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${nameCounterOverflow}`, - { align: "left", size: "normal" }, - true, - ), - ), - ); - } - } else { - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${name}`, - { align: "left", size: "normal" }, - true, - ), - ), - ); - } - - let priceString = formatNumber( - parseFloat(val.price, 10), - ).toString(); - let qtyString = val.qty.toString(); - let amountString = formatNumber( - parseFloat(val.price, 10) * parseFloat(val.qty, 10), - ).toString(); - - for (let ps = 0; ps < 12 - priceString.length; ps += 1) { - finalLines = finalLines + " "; - } - - finalLines = finalLines + priceString; - - for (let qt = 0; qt < 6 - qtyString.length; qt += 1) { - finalLines = finalLines + " "; - } - finalLines = finalLines + qtyString; - - for (let as = 0; as < 14 - amountString.length; as += 1) { - finalLines = finalLines + " "; - } - - finalLines = finalLines + amountString; - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${finalLines}`, - { align: "left", size: "normal" }, - true, - ), - ), - ); - totalPurchase = - parseFloat(totalPurchase, 10) + - parseFloat(val.price, 10) * parseFloat(val.qty, 10); - }); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - "================================", - { align: "left", size: "normal", weight: "bold" }, - true, - ), - ), - ); - - let subTotal = strings.Subtotal; - let sub = formatNumber( - parseFloat( - this.props.receiptStore.defaultReceipt.subtotal, - 10, - ), - ).toString(); - for (let t = 0; t < 23 - sub.length; t += 1) { - subTotal = subTotal + " "; - } - subTotal = subTotal + sub; - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${subTotal}`, - { align: "left", size: "normal" }, - true, - ), - ), - ); - let taxValue = strings.Tax; - let tax = formatNumber( - parseFloat( - this.props.receiptStore.defaultReceipt.get_tax_total, - 10, - ), - ).toString(); - for (let t = 0; t < 29 - tax.length; t += 1) { - taxValue = taxValue + " "; - } - taxValue = taxValue + tax; - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${taxValue}`, - { align: "left", size: "normal" }, - true, - ), - ), - ); - let discountValue = strings.Discount; - let discount = formatNumber( - parseFloat( - this.props.receiptStore.defaultReceipt.discounts, - 10, - ), - ).toString(); - for (let d = 0; d < 24 - discount.length; d += 1) { - discountValue = discountValue + " "; - } - discountValue = discountValue + discount; - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${discountValue}`, - { align: "left", size: "normal" }, - true, - ), - ), - ); - - let commissionValue = strings.Commission; - - let commission_total = formatNumber( - parseFloat(commission_toto, 10), - ).toString(); - for (let d = 0; d < 22 - commission_total.length; d += 1) { - commissionValue = commissionValue + " "; - } - commissionValue = commissionValue + commission_total; - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${commissionValue}`, - { align: "left", size: "normal" }, - true, - ), - ), - ); - - let total = ""; - total = total + strings.TotalAmount; - - for ( - let totalLength = 0; - totalLength < - 20 - - formatNumber(parseFloat(totalPurchase, 10)).toString() - .length; - totalLength += 1 - ) { - total = total + " "; - } - total = - total + - formatNumber( - parseFloat(totalPurchase, 10) - - parseFloat( - this.props.receiptStore.defaultReceipt.discounts, - 10, - ) + - parseFloat( - this.props.receiptStore.defaultReceipt.get_tax_total, - 10, - ), - ).toString(); - - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${total}`, - { align: "left", size: "normal", weight: "bold" }, - true, - ), - ), - ); - let cash = strings.Cash; - for ( - let cashLength = 0; - cashLength < - 28 - - formatNumber( - parseFloat(this.props.stateStore.payment_value, 10), - ).toString().length; - cashLength += 1 - ) { - cash = cash + " "; - } - cash = - cash + - formatNumber( - parseFloat(this.props.stateStore.payment_value, 10), - ).toString(); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${cash}`, - { align: "left", size: "normal", weight: "bold" }, - true, - ), - ), - ); - let change = strings.Change; - let changeValue = formatNumber( - parseFloat( - parseFloat(this.props.stateStore.payment_value, 10) - - (parseFloat(totalPurchase, 10) - - parseFloat( - this.props.receiptStore.defaultReceipt.discounts, - 10, - ) + - parseFloat( - this.props.receiptStore.defaultReceipt - .get_tax_total, - 10, - )), - 10, - ), - ).toString(); - for ( - let changeLength = 0; - changeLength < 26 - changeValue.length; - changeLength += 1 - ) { - change = change + " "; - } - change = change + changeValue; - - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${change}`, - { align: "left", size: "normal", weight: "bold" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - "================================", - { size: "normal" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - strings.ThisServesAsYour, - { align: "center", size: "doubleheight" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - strings.OfficialReceipt + "\n", - { align: "center", size: "doubleheight" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - `${ - this.props.printerStore.companySettings.length > 0 - ? this.props.printerStore.companySettings[0].footer.toString() - : "" - }`, - { align: "center", size: "normal" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - "\n" + - strings.POSProvider + - "Bai Web and Mobile Lab\n" + - "Insular Life Bldg, Don Apolinar\n" + - "Velez cor. Oldarico Akut St.,\n" + - "Cagayan de Oro, 9000,\n" + - "Misamis Oriental\n" + - strings.AccredNo + - strings.DateIssued + - strings.ValidUntil, - { align: "left", size: "normal" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - strings.ThisReceiptShallBeValidFor + - strings.FiveYearsFromTheDateOf + - strings.ThePermitToUse, - { align: "center", size: "normal" }, - true, - ), - ), - ); - - // Add 3 new lines - writePromises.push( - BluetoothSerial.write(TinyPOS.bufferedLine(3)), - ); - - // Push drawer - writePromises.push( - BluetoothSerial.write(TinyPOS.kickCashDrawer()), - ); - writePromises.push( - BluetoothSerial.write(TinyPOS.kickCashDrawer()), - ); - - Promise.all(writePromises) - .then(res2 => { - receiptCurrent.completed( - this.props.attendantStore.defaultAttendant.user_name, - ); - - this.props.receiptStore.defaultReceipt.changeTaxesAmount( - this.props.receiptStore.defaultReceipt.get_tax_total, - ); - // add to row - this.props.paymentStore.add({ - receipt: this.props.receiptStore.defaultReceipt._id.toString(), - date: Date.now(), - paid: parseInt(this.props.stateStore.payment_value, 10), - type: this.props.stateStore.payment_state[0].selected, - dateUpdated: Date.now(), - syncStatus: false, - }); - - // Reset payment amount - // this.setState({ - // modalVisible: false, - // paymentAmount: 0, - // }); - this.props.stateStore.changeValue( - "modalVisible", - false, - "Payment", - ); - this.props.stateStore.changeValue( - "paymentAmount", - 0, - "Payment", - ); - - Toast.show({ - text: strings.TransactionCompleted, - duration: 5000, - }); - }) - .catch(err => { - receiptCurrent.completed( - this.props.attendantStore.defaultAttendant.user_name, - ); - - this.props.receiptStore.defaultReceipt.changeTaxesAmount( - this.props.receiptStore.defaultReceipt.get_tax_total, - ); - - // add to row - this.props.paymentStore.add({ - receipt: this.props.receiptStore.defaultReceipt._id.toString(), - date: Date.now(), - paid: parseInt(this.props.stateStore.payment_value, 10), - type: this.props.stateStore.payment_state[0].selected, - dateUpdated: Date.now(), - syncStatus: false, - }); - // this.setState({ - // modalVisible: false, - // paymentAmount: 0, - // }); - this.props.stateStore.changeValue( - "modalVisible", - false, - "Payment", - ); - this.props.stateStore.changeValue( - "paymentAmount", - 0, - "Payment", - ); - Toast.show({ - text: err.message + strings.TransactionCompleted, - buttonText: strings.Okay, - position: "bottom", - duration: 5000, - }); - }); - } else { - receiptCurrent.completed( - this.props.attendantStore.defaultAttendant.user_name, - ); - - this.props.receiptStore.defaultReceipt.changeTaxesAmount( - this.props.receiptStore.defaultReceipt.get_tax_total, - ); - - // add to row - this.props.paymentStore.add({ - receipt: this.props.receiptStore.defaultReceipt._id.toString(), - date: Date.now(), - paid: parseInt(this.props.stateStore.payment_value, 10), - type: this.props.stateStore.payment_state[0].selected, - dateUpdated: Date.now(), - syncStatus: false, - }); - - // this.setState({ - // modalVisible: false, - // paymentAmount: 0, - // }); - this.props.stateStore.changeValue( - "modalVisible", - false, - "Payment", - ); - this.props.stateStore.changeValue( - "paymentAmount", - 0, - "Payment", - ); - Toast.show({ - text: - strings.TransactionCompleted[ - strings.UnableToConnectPrinter - ], - buttonText: strings.Okay, - position: "bottom", - duration: 6000, - }); - } - - const { defaultShift } = this.props.shiftStore; - - // If shift started and shift hasn't ended - if (defaultShift.shiftStarted && !defaultShift.shiftEnded) { - // Set the default receipt - const { defaultReceipt } = this.props.receiptStore; - - // set shift - defaultReceipt.setShift(defaultShift._id); - - const { ending_cash } = defaultShift; - - // Set the end cash - defaultShift.setEndCash( - ending_cash + defaultReceipt.netTotal, - ); - } - - // this.props.receiptStore.defaultReceipt.clear(); - this.props.receiptStore.add( - this.props.receiptStore.defaultReceipt, - ); - this.props.receiptStore.setPreviousReceipt( - this.props.receiptStore.defaultReceipt, - ); - let discountValueForDisplay = this.props.receiptStore - .defaultReceipt.discounts; - let taxesValueForDisplay = this.props.receiptStore - .defaultReceipt.get_tax_total; - this.props.receiptStore.newReceipt( - this.props.printerStore.companySettings[0].tax, - ); - this.props.receiptStore.setLastScannedBarcode(""); - this.props.receiptStore.unselectReceiptLine(); - this.props.navigation.navigate("Sales", { - cash: this.props.stateStore.payment_value, - change: parseFloat( - parseFloat(this.props.stateStore.payment_value, 10) - - (parseFloat(totalPurchase, 10) - - parseFloat(discountValueForDisplay, 10) + - parseFloat(taxesValueForDisplay, 10)), - 10, - ), - }); - }, - }, - ], - ); - }); - } + const { defaultReceipt } = this.props.receiptStore; + const { defaultAttendant } = this.props.attendantStore; + defaultReceipt.setAttendant(defaultAttendant.user_name); + on_pay(this.props); }; onBack() { @@ -1057,22 +178,14 @@ export default class PaymentContainer extends React.Component { } navigation = () => { + this.props.stateStore.setPaymentValue("0"); + this.props.stateStore.setMopAmount("0"); + const { stateStore } = this.props; + stateStore.resetPaymentTypes(); + unregister_tag_event(); this.getBluetoothState(true); this.onBack(); }; - // DEPRECATED - // onPrinterChange(value) { - // this.props.stateStore.changeValue("itemSelected", value, "Payment"); - // BluetoothSerial.connect("DC:0D:30:0B:77:B1") - // .then(res => { - // this.props.stateStore.changeValue("connection", true, "Payment"); - // }) - // .catch(() => { - // // this.setState({ connection: false }); - // this.props.stateStore.changeValue("connection", false, "Payment"); - // }); - // } - onPrinterPress = () => { this.props.navigation.navigate("Settings"); }; @@ -1203,13 +316,72 @@ export default class PaymentContainer extends React.Component { this.props.stateStore.changeValue("customerPhoneNumber", "", "Payment"); this.props.stateStore.changeValue("customerNotes", "", "Payment"); }; - + get_payment_total = () => { + let payment_data = JSON.parse(this.props.stateStore.payment_types); + let total = 0; + for (let i = 0; i < payment_data.length; i += 1) { + total += parseFloat(payment_data[i].amount, 10); + } + return total; + }; + addMultipleMop = () => { + const { stateStore } = this.props; + if (parseFloat(this.props.stateStore.payment_value, 10) > 0) { + stateStore.updatePaymentType({ + type: this.props.stateStore.payment_state[0].selected, + amount: parseFloat(this.props.stateStore.payment_value, 10), + }); + stateStore.setMopAmount(this.get_payment_total().toString()); + stateStore.setBalance( + ( + parseFloat(stateStore.amount_due, 10) - + parseFloat(this.get_payment_total(), 10) + ).toString(), + ); + stateStore.setPaymentValue("0"); + } else { + Toast.show({ + text: "Please input amount greater than 0", + buttonText: strings.Okay, + position: "bottom", + duration: 6000, + }); + } + }; + removeMop = () => { + const { stateStore } = this.props; + stateStore.removePaymentType(); + stateStore.setMopAmount(this.get_payment_total().toString()); + stateStore.setBalance( + ( + parseFloat(stateStore.amount_due, 10) - + parseFloat(this.get_payment_total(), 10) + ).toString(), + ); + }; + proceedToWalletTransaction = () => { + const { + scanned_nfc, + customers_pin_value, + deviceId, + } = this.props.stateStore; + check_customers_pin(scanned_nfc, customers_pin_value, this.props, deviceId); + }; + clearCustomersPin = () => { + const { set_customers_pin } = this.props.stateStore; + set_customers_pin(""); + }; render() { strings.setLanguage(currentLanguage().companyLanguage); return ( + this.controller.onChangePayment(payment, this.props) + } onChangeCustomerName={this.controller.onChangeCustomerName} onChangeCustomerEmail={this.controller.onChangeCustomerEmail} onChangeCustomerPhoneNumber={this.onChangeCustomerPhoneNumber} @@ -1239,6 +414,10 @@ export default class PaymentContainer extends React.Component { } useDefaultCustomer={this.props.stateStore.useDefaultCustomer} isCurrencyDisabled={this.props.stateStore.isCurrencyDisabled} + settings_state={this.props.stateStore.settings_state[0]} + addMultipleMop={this.addMultipleMop} + removeMop={this.removeMop} + proceedToWalletTransaction={this.proceedToWalletTransaction} /> ); } diff --git a/src/container/PaymentContainer/nfc_manager_initialization.js b/src/container/PaymentContainer/nfc_manager_initialization.js new file mode 100644 index 0000000..f8fcc14 --- /dev/null +++ b/src/container/PaymentContainer/nfc_manager_initialization.js @@ -0,0 +1,273 @@ +import { Alert } from "react-native"; +import NfcManager from "react-native-nfc-manager"; +import { showToastDanger, showToast } from "../../utils"; +import config from "../../boot/configureStore"; +import { on_pay } from "./on_pay"; +import { NetInfo } from "react-native"; + +import FrappeFetch from "react-native-frappe-fetch"; +let validUrl = require("valid-url"); +const stores = config(); + +export function nfc_initialization(props, deviceId) { + NfcManager.isSupported().then(result => { + if (result) { + check_tagged_nfc_card(props, deviceId); + } else { + showToastDanger("Device does not support NFC"); + } + }); +} + +export function check_tagged_nfc_card(props, deviceId) { + NfcManager.isEnabled().then(status => { + if (status) { + register_tag_event(props, deviceId); + } else { + showToastDanger("NFC is disabled"); + } + }); +} + +export function register_tag_event(props, deviceId) { + const nfc_props = { + invalidateAfterFirstRead: true, + isReaderModeEnabled: true, + }; + const message = "Scanning NFC Card"; + showToast("Please Scan Customer Card Now"); + NfcManager.registerTagEvent( + tag => validate_tag_event(tag, props, deviceId), + message, + nfc_props, + ); +} + +export function unregister_tag_event() { + NfcManager.unregisterTagEvent(); +} + +export function set_attendant(props) { + const { defaultReceipt } = props.receiptStore; + const { defaultAttendant } = props.attendantStore; + defaultReceipt.setAttendant(defaultAttendant.user_name); +} + +export async function validate_tag_event(tag, props, deviceId) { + set_attendant(props); + check_internet_connection(tag, props, deviceId); +} + +export async function check_internet_connection(tag, props, deviceId) { + NetInfo.isConnected.fetch().then(async isConnected => { + if (isConnected) { + on_check_tag_event(tag, props, deviceId); + } else { + showToastDanger("No Internet Connection. Please Check"); + } + }); +} + +export async function on_check_tag_event(tag, props, deviceId) { + let scanned_nfc = JSON.parse(props.stateStore.scanned_nfc); + if (!("customer" in scanned_nfc)) { + check_customer_tag(tag, props, deviceId); + } else if ( + !("attendant" in scanned_nfc) && + props.stateStore.customers_pin_value + ) { + check_attendant_tag(tag, props, deviceId); + } else if ( + !("attendant" in scanned_nfc) && + !props.stateStore.customers_pin_value + ) { + showToastDanger("Please enter customers pin first"); + } +} +export async function check_customers_pin( + scanned_nfc, + customers_pin, + props, + deviceId, +) { + if (scanned_nfc) { + if (validUrl.isWebUri(returnUrl().url)) { + FrappeFetch.createClient(returnUrl()) + .then(() => { + const { Client } = FrappeFetch; + return Client.postApi( + "tailpos_sync.wallet_sync.check_customers_pin", + { + wallet_card_number: scanned_nfc, + customers_pin: customers_pin, + }, + ); + }) + .catch(() => {}) + .then(response => response.json()) + .then(responseJson => { + if (responseJson.message.failed) { + showToastDanger(responseJson.message.message); + } else { + showToast(responseJson.message.message); + props.stateStore.is_customers_pin(); + } + }) + .catch(() => + showToastDanger( + "Please check your credentials in Sync settings and Error Logs in ERPNext", + ), + ); + } else { + showToastDanger("Invalid URL. Please set valid URL in Sync Settings"); + } + } +} +export async function check_attendant_tag(tag, props, deviceId) { + if (tag) { + if (validUrl.isWebUri(returnUrl().url)) { + FrappeFetch.createClient(returnUrl()) + .then(() => { + const { Client } = FrappeFetch; + return Client.postApi( + "tailpos_sync.wallet_sync.validate_if_attendant_wallet_exists", + { + wallet_card_number: tag.id, + }, + ); + }) + .catch(() => {}) + .then(response => response.json()) + .then(responseJson => { + if (responseJson.message.failed) { + showToastDanger(responseJson.message.message); + } else { + props.stateStore.updateScannedNfc("attendant", tag.id); + + proceed_to_validate_sync(props, deviceId); + } + }) + .catch(() => + showToastDanger( + "Please check your credentials in Sync settings and Error Logs in ERPNext", + ), + ); + } else { + showToastDanger("Invalid URL. Please set valid URL in Sync Settings"); + } + } +} +export async function proceed_to_validate_sync(props, deviceId) { + Alert.alert( + "Confirm Wallet Transaction", + "Are you sure you want to proceed wallet transaction?", + [ + { text: "No", style: "cancel" }, + { + text: "Yes", + onPress: () => { + on_sync_tag_event(props.stateStore.scanned_nfc, props, deviceId); + }, + }, + ], + ); +} +export async function check_customer_tag(tag, props, deviceId) { + if (tag) { + const { defaultReceipt } = stores.receiptStore; + if (validUrl.isWebUri(returnUrl().url)) { + FrappeFetch.createClient(returnUrl()) + .then(() => { + const { Client } = FrappeFetch; + return Client.postApi( + "tailpos_sync.wallet_sync.validate_if_customer_wallet_exists", + { + wallet_card_number: tag.id, + receipt: json_object(defaultReceipt), + }, + ); + }) + + .catch(() => {}) + .then(response => response.json()) + .then(responseJson => { + if (responseJson.message.failed) { + showToastDanger(responseJson.message.message); + } else { + props.stateStore.updateScannedNfc("customer", tag.id); + showToast(responseJson.message.message); + } + }) + + .catch(() => + showToastDanger( + "Please check your credentials in Sync settings and Error Logs in ERPNext", + ), + ); + } else { + showToastDanger("Invalid URL. Please set valid URL in Sync Settings"); + } + } +} + +export async function on_sync_tag_event(scanned_nfc, props, deviceId) { + if (scanned_nfc) { + const { defaultReceipt } = stores.receiptStore; + if (validUrl.isWebUri(returnUrl().url)) { + FrappeFetch.createClient(returnUrl()) + .then(() => { + const { Client } = FrappeFetch; + return Client.postApi("tailpos_sync.wallet_sync.validate_wallet", { + wallet: scanned_nfc, + receipt: json_object(defaultReceipt), + device_id: deviceId, + }); + }) + .catch(() => {}) + .then(response => response.json()) + .then(responseJson => { + validate_return_from_server(responseJson.message, props); + }) + .catch(() => + showToastDanger( + "Please check your credentials in Sync settings and Error Logs in ERPNext", + ), + ); + } else { + showToastDanger("Invalid URL. Please set valid URL in Sync Settings"); + } + } +} + +export function validate_return_from_server(data, props) { + if (data.failed) { + showToastDanger(data.message); + } else { + showToast("Wallet Scanned Successfully"); + + stores.navigation = props.navigation; + on_pay(stores); + } +} +export function json_object(obj) { + let receipt_json_object = {}; + Object.keys(obj).forEach(function(key) { + if (!(key === "_id")) { + receipt_json_object[key] = obj[key]; + } + }); + + return receipt_json_object; +} +export function returnUrl() { + const { url, user_name, password, isHttps } = stores.printerStore.sync[0]; + + const protocol = isHttps ? "https://" : "http://"; + let site_url = protocol + url; + + return { + url: site_url.toLowerCase(), + username: user_name, + password: password, + }; +} diff --git a/src/container/PaymentContainer/on_pay.js b/src/container/PaymentContainer/on_pay.js new file mode 100644 index 0000000..e9fae32 --- /dev/null +++ b/src/container/PaymentContainer/on_pay.js @@ -0,0 +1,900 @@ +import { Alert } from "react-native"; +import TinyPOS from "tiny-esc-pos"; +import { formatNumber } from "accounting-js"; +import BluetoothSerial from "react-native-bluetooth-serial"; +import { Toast } from "native-base"; +import translation from "../../translations/translation"; +import LocalizedStrings from "react-native-localization"; +let strings = new LocalizedStrings(translation); +const moment = require("moment"); + +export async function setOrderCompleted(props) { + const { queueOrigin, currentTable, setCurrentTable } = props.stateStore; + + const url = `${queueOrigin}/api/v1/complete_order`; + const fetchData = { + method: "POST", + body: JSON.stringify({ + id: currentTable, + }), + }; + + fetch(url, fetchData) + .then(res => res.json()) + .then(res => setCurrentTable(-1)); +} +export async function on_pay(props) { + const paymentValue = props.stateStore.settings_state[0].multipleMop + ? parseFloat(props.stateStore.payment_amount) + : parseFloat(props.stateStore.payment_value); + const amountDue = parseFloat(props.stateStore.amount_due); + + if (paymentValue < amountDue) { + Alert.alert( + strings.Alert, + strings.AmountPaidMustBeGreaterThanOrEqualToAmountDue, + ); + } else if (paymentValue >= amountDue) { + let receiptNumber = await props.receiptStore.numberOfReceipts(); + let receiptNumberLength = receiptNumber.toString().length; + let finalReceiptNumber = ""; + for ( + let lengthNumber = 0; + lengthNumber < 15 - receiptNumberLength; + lengthNumber += 1 + ) { + finalReceiptNumber = finalReceiptNumber + "0"; + } + finalReceiptNumber = finalReceiptNumber + receiptNumber.toString(); + + const receiptCurrent = props.receiptStore.defaultReceipt; + const { deviceId } = props.stateStore; + receiptCurrent.setRoundOff( + props.stateStore.settings_state[0].allowRoundOff, + ); + if (deviceId) { + receiptCurrent.setDeviceId(deviceId); + } + + BluetoothSerial.isConnected().then(res => { + let totalPurchase = 0.0; + Alert.alert( + strings.ReceiptConfirmation, // title + strings.DoYouWantToPrintReceipt, + [ + { + text: strings.No, + style: "cancel", + onPress: () => { + setOrderCompleted(props); + props.shiftStore.defaultShift.addTotalDiscount( + receiptCurrent.discounts, + ); + props.shiftStore.defaultShift.addTotalTaxes( + parseFloat(props.receiptStore.defaultReceipt.subtotal) * + (parseFloat(receiptCurrent.taxesValue) / 100), + ); + props.shiftStore.defaultShift.addNumberOfTransaction(); + + let totalAmountDue = 0.0; + + props.receiptStore.defaultReceipt.lines.map(val => { + totalAmountDue = + parseInt(totalAmountDue, 10) + + parseInt(val.price.toFixed(2), 10) * + parseInt(val.qty.toFixed(2), 10); + if (val.category && val.category !== "No Category") { + props.shiftStore.defaultShift.categoriesAmounts({ + name: val.category, + total_amount: + parseInt(val.price.toFixed(2), 10) * + parseInt(val.qty.toFixed(2), 10), + }); + } + if (props.stateStore.payment_state[0].selected) { + props.shiftStore.defaultShift.mopAmounts({ + name: props.stateStore.payment_state[0].selected, + total_amount: + parseInt(val.price.toFixed(2), 10) * + parseInt(val.qty.toFixed(2), 10), + }); + } + }); + if (props.receiptStore.defaultReceipt.orderType !== "None") { + props.shiftStore.defaultShift.addOrderType({ + amount: parseFloat(totalAmountDue, 10), + type: props.receiptStore.defaultReceipt.orderType, + }); + } + props.shiftStore.defaultShift.addTotalSales(totalAmountDue); + props.receiptStore.defaultReceipt.lines.map(val => { + totalPurchase = + parseFloat(totalPurchase, 10) + + parseFloat(val.price, 10) * parseFloat(val.qty, 10); + }); + + receiptCurrent.completed( + props.attendantStore.defaultAttendant.user_name, + ); + const { defaultShift } = props.shiftStore; + + // If shift started and shift hasn't ended + if (defaultShift.shiftStarted && !defaultShift.shiftEnded) { + // Set the default receipt + const { defaultReceipt } = props.receiptStore; + + // set shift + defaultReceipt.setShift(defaultShift._id); + + const { ending_cash } = defaultShift; + + // Set the end cash + defaultShift.setEndCash(ending_cash + defaultReceipt.netTotal); + } + + props.receiptStore.defaultReceipt.changeTaxesAmount( + props.stateStore.enableOverallTax + ? props.receiptStore.defaultReceipt.get_tax_total + : props.receiptStore.defaultReceipt + .get_tax_total_based_on_each_item, + ); + + // props.receiptStore.defaultReceipt.clear(); + payment_add(props); + + props.receiptStore.add(props.receiptStore.defaultReceipt); + props.receiptStore.setPreviousReceipt( + props.receiptStore.defaultReceipt, + ); + let discountValueForDisplay = + props.receiptStore.defaultReceipt.discounts; + let taxesValueForDisplay = props.stateStore.enableOverallTax + ? props.receiptStore.defaultReceipt.get_tax_total + : props.receiptStore.defaultReceipt + .get_tax_total_based_on_each_item; + props.receiptStore.newReceipt( + props.printerStore.companySettings[0].tax, + ); + props.receiptStore.setLastScannedBarcode(""); + props.receiptStore.unselectReceiptLine(); + props.stateStore.changeValue("selected", "Cash", "Payment"); + change_navigation( + props, + totalPurchase, + discountValueForDisplay, + taxesValueForDisplay, + ); + }, + }, + { + text: strings.Yes, + onPress: () => { + setOrderCompleted(props); + props.shiftStore.defaultShift.addTotalDiscount( + receiptCurrent.discounts, + ); + props.shiftStore.defaultShift.addTotalTaxes( + parseFloat(props.receiptStore.defaultReceipt.subtotal) * + (parseFloat(receiptCurrent.taxesValue) / 100), + ); + props.shiftStore.defaultShift.addNumberOfTransaction(); + + // Let me print first + let totalAmountDue = 0.0; + let commission_toto = 0.0; + props.receiptStore.defaultReceipt.lines.map(val => { + // const { defaultShift } = props.shiftStore; + let ComHolder = JSON.parse(val.commission_details); + ComHolder.map(val2 => { + commission_toto = + commission_toto + parseInt(val2.commission_amount, 10); + }); + // defaultShift.addCommission( + // parseInt(val.commission_amount, 10), + // ); + totalAmountDue = + parseInt(totalAmountDue, 10) + + parseInt(val.price.toFixed(2), 10) * + parseInt(val.qty.toFixed(2), 10); + if (val.category && val.category !== "No Category") { + props.shiftStore.defaultShift.categoriesAmounts({ + name: val.category, + total_amount: + parseInt(val.price.toFixed(2), 10) * + parseInt(val.qty.toFixed(2), 10), + }); + } + if (props.stateStore.payment_state[0].selected) { + props.shiftStore.defaultShift.mopAmounts({ + name: props.stateStore.payment_state[0].selected, + total_amount: + parseInt(val.price.toFixed(2), 10) * + parseInt(val.qty.toFixed(2), 10), + }); + } + }); + if (props.receiptStore.defaultReceipt.orderType !== "None") { + props.shiftStore.defaultShift.addOrderType({ + amount: parseFloat(totalAmountDue, 10), + type: props.receiptStore.defaultReceipt.orderType, + }); + } + props.shiftStore.defaultShift.addTotalSales(totalAmountDue); + if (res) { + let writePromises = []; + + for ( + let printedReceipts = 0; + printedReceipts < + parseInt( + props.printerStore.companySettings[0].changeNoReceipts, + 10, + ); + printedReceipts += 1 + ) { + writePromises = []; + + writePromises.push(BluetoothSerial.write(TinyPOS.init())); + + // Header + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${ + props.printerStore.companySettings.length > 0 + ? props.printerStore.companySettings[0].name + ? props.printerStore.companySettings[0].name.toString() + : "" + : "" + }`, + { align: "center", size: "doubleheight" }, + true, + ), + ), + ); + + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${ + props.printerStore.companySettings.length > 0 + ? props.printerStore.companySettings[0].header.toString() + : "" + }`, + { align: "center", size: "normal" }, + true, + ), + ), + ); + + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + "================================", + { size: "normal" }, + true, + ), + ), + ); + + // Date + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${moment().format("YYYY/MM/D hh:mm:ss SSS")}`, + { size: "normal" }, + true, + ), + ), + ); + + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + "================================", + { size: "normal" }, + true, + ), + ), + ); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + strings.Cashier + + `${props.attendantStore.defaultAttendant.user_name}`, + { align: "left", size: "normal" }, + true, + ), + ), + ); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + strings.TransactionNo + `${finalReceiptNumber}`, + { align: "left", size: "normal" }, + true, + ), + ), + ); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + "================================", + { size: "normal" }, + true, + ), + ), + ); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + "Mode of payment: " + + props.stateStore.payment_state[0].selected, + { align: "left", size: "normal" }, + true, + ), + ), + ); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + "================================", + { size: "normal" }, + true, + ), + ), + ); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + strings.Purchases, + { align: "center", size: "normal" }, + true, + ), + ), + ); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + strings.Items + + " " + + strings.Amount + + " ", + { align: "left", size: "normal", weight: "bold" }, + true, + ), + ), + ); + + props.receiptStore.defaultReceipt.lines.map(val => { + let finalLines = ""; + + const name = val.item_name; + + if (name.length > 14) { + let quotientValue = name.length / 14; + for ( + let quotient = 0; + quotient < parseInt(quotientValue, 10); + quotient += 1 + ) { + let currentCounter = quotient * 14; + let nameCounter = ""; + for ( + let n = currentCounter; + n < (quotient + 1) * 14; + n += 1 + ) { + nameCounter = nameCounter + name[n]; + } + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${nameCounter}`, + { align: "left", size: "normal" }, + true, + ), + ), + ); + } + if (name.length - parseInt(quotientValue, 10) * 14 > 0) { + let nameCounterOverflow = ""; + for ( + let m = parseInt(quotientValue, 10) * 14; + m < name.length; + m += 1 + ) { + nameCounterOverflow = nameCounterOverflow + name[m]; + } + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${nameCounterOverflow}`, + { align: "left", size: "normal" }, + true, + ), + ), + ); + } + } else { + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${name}`, + { align: "left", size: "normal" }, + true, + ), + ), + ); + } + + let priceString = formatNumber( + parseFloat(val.price, 10), + ).toString(); + let qtyString = val.qty.toString(); + let amountString = formatNumber( + parseFloat(val.price, 10) * parseFloat(val.qty, 10), + ).toString(); + + for (let ps = 0; ps < 12 - priceString.length; ps += 1) { + finalLines = finalLines + " "; + } + + finalLines = finalLines + priceString; + + for (let qt = 0; qt < 6 - qtyString.length; qt += 1) { + finalLines = finalLines + " "; + } + finalLines = finalLines + qtyString; + + for (let as = 0; as < 14 - amountString.length; as += 1) { + finalLines = finalLines + " "; + } + + finalLines = finalLines + amountString; + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${finalLines}`, + { align: "left", size: "normal" }, + true, + ), + ), + ); + if (printedReceipts === 0) { + totalPurchase = + parseFloat(totalPurchase, 10) + + parseFloat(val.price, 10) * parseFloat(val.qty, 10); + } + }); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + "================================", + { align: "left", size: "normal", weight: "bold" }, + true, + ), + ), + ); + + let subTotal = strings.Subtotal; + let sub = formatNumber( + parseFloat(props.receiptStore.defaultReceipt.subtotal, 10), + ).toString(); + for (let t = 0; t < 23 - sub.length; t += 1) { + subTotal = subTotal + " "; + } + subTotal = subTotal + sub; + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${subTotal}`, + { align: "left", size: "normal" }, + true, + ), + ), + ); + let taxValue = strings.Tax; + let tax = formatNumber( + parseFloat( + props.stateStore.enableOverallTax + ? props.receiptStore.defaultReceipt.get_tax_total + : props.receiptStore.defaultReceipt + .get_tax_total_based_on_each_item, + 10, + ), + ).toString(); + for (let t = 0; t < 29 - tax.length; t += 1) { + taxValue = taxValue + " "; + } + taxValue = taxValue + tax; + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${taxValue}`, + { align: "left", size: "normal" }, + true, + ), + ), + ); + let discountValue = strings.Discount; + let discount = formatNumber( + parseFloat(props.receiptStore.defaultReceipt.discounts, 10), + ).toString(); + for (let d = 0; d < 24 - discount.length; d += 1) { + discountValue = discountValue + " "; + } + discountValue = discountValue + discount; + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${discountValue}`, + { align: "left", size: "normal" }, + true, + ), + ), + ); + + let commissionValue = strings.Commission; + + let commission_total = formatNumber( + parseFloat(commission_toto, 10), + ).toString(); + for (let d = 0; d < 22 - commission_total.length; d += 1) { + commissionValue = commissionValue + " "; + } + commissionValue = commissionValue + commission_total; + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${commissionValue}`, + { align: "left", size: "normal" }, + true, + ), + ), + ); + + let total = ""; + total = total + strings.TotalAmount; + + for ( + let totalLength = 0; + totalLength < + 20 - + formatNumber(parseFloat(totalPurchase, 10)).toString() + .length; + totalLength += 1 + ) { + total = total + " "; + } + total = + total + + formatNumber( + parseFloat(totalPurchase, 10) - + parseFloat( + props.receiptStore.defaultReceipt.discounts, + 10, + ) + + parseFloat( + props.stateStore.enableOverallTax + ? props.receiptStore.defaultReceipt.get_tax_total + : props.receiptStore.defaultReceipt + .get_tax_total_based_on_each_item, + 10, + ), + ).toString(); + + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${total}`, + { align: "left", size: "normal", weight: "bold" }, + true, + ), + ), + ); + let cash = strings.Cash; + for ( + let cashLength = 0; + cashLength < + 28 - + formatNumber( + props.stateStore.settings_state[0].multipleMop + ? parseFloat(props.stateStore.payment_amount) + : parseFloat(props.stateStore.payment_value, 10), + ).toString().length; + cashLength += 1 + ) { + cash = cash + " "; + } + cash = + cash + + formatNumber( + props.stateStore.settings_state[0].multipleMop + ? parseFloat(props.stateStore.payment_amount) + : parseFloat(props.stateStore.payment_value, 10), + ).toString(); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${cash}`, + { align: "left", size: "normal", weight: "bold" }, + true, + ), + ), + ); + let change = strings.Change; + let changeValue = formatNumber( + parseFloat( + (props.stateStore.settings_state[0].multipleMop + ? parseFloat(props.stateStore.payment_amount) + : parseFloat(props.stateStore.payment_value, 10)) - + (parseFloat(totalPurchase, 10) - + parseFloat( + props.receiptStore.defaultReceipt.discounts, + 10, + ) + + parseFloat( + props.stateStore.enableOverallTax + ? props.receiptStore.defaultReceipt.get_tax_total + : props.receiptStore.defaultReceipt + .get_tax_total_based_on_each_item, + 10, + )), + 10, + ), + ).toString(); + for ( + let changeLength = 0; + changeLength < 26 - changeValue.length; + changeLength += 1 + ) { + change = change + " "; + } + change = change + changeValue; + + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${change}`, + { align: "left", size: "normal", weight: "bold" }, + true, + ), + ), + ); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + "================================", + { size: "normal" }, + true, + ), + ), + ); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + strings.ThisServesAsYour, + { align: "center", size: "doubleheight" }, + true, + ), + ), + ); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + strings.OfficialReceipt + "\n", + { align: "center", size: "doubleheight" }, + true, + ), + ), + ); + writePromises.push( + BluetoothSerial.write( + TinyPOS.bufferedText( + `${ + props.printerStore.companySettings.length > 0 + ? props.printerStore.companySettings[0].footer.toString() + : "" + }`, + { align: "center", size: "normal" }, + true, + ), + ), + ); + // Add 3 new lines + writePromises.push( + BluetoothSerial.write(TinyPOS.bufferedLine(3)), + ); + } + // Push drawer + writePromises.push( + BluetoothSerial.write(TinyPOS.kickCashDrawer()), + ); + writePromises.push( + BluetoothSerial.write(TinyPOS.kickCashDrawer()), + ); + Promise.all(writePromises) + .then(res2 => { + receiptCurrent.completed( + props.attendantStore.defaultAttendant.user_name, + ); + + props.receiptStore.defaultReceipt.changeTaxesAmount( + props.stateStore.enableOverallTax + ? props.receiptStore.defaultReceipt.get_tax_total + : props.receiptStore.defaultReceipt + .get_tax_total_based_on_each_item, + ); + // add to row + + payment_add(props); + + // Reset payment amount + // setState({ + // modalVisible: false, + // paymentAmount: 0, + // }); + props.stateStore.changeValue( + "modalVisible", + false, + "Payment", + ); + props.stateStore.changeValue("paymentAmount", 0, "Payment"); + + Toast.show({ + text: strings.TransactionCompleted, + duration: 5000, + }); + }) + .catch(err => { + receiptCurrent.completed( + props.attendantStore.defaultAttendant.user_name, + ); + + props.receiptStore.defaultReceipt.changeTaxesAmount( + props.stateStore.enableOverallTax + ? props.receiptStore.defaultReceipt.get_tax_total + : props.receiptStore.defaultReceipt + .get_tax_total_based_on_each_item, + ); + + payment_add(props); + props.stateStore.changeValue( + "modalVisible", + false, + "Payment", + ); + props.stateStore.changeValue("paymentAmount", 0, "Payment"); + + Toast.show({ + text: err.message + strings.TransactionCompleted, + buttonText: strings.Okay, + position: "bottom", + duration: 5000, + }); + }); + } else { + receiptCurrent.completed( + props.attendantStore.defaultAttendant.user_name, + ); + + props.receiptStore.defaultReceipt.changeTaxesAmount( + props.stateStore.enableOverallTax + ? props.receiptStore.defaultReceipt.get_tax_total + : props.receiptStore.defaultReceipt + .get_tax_total_based_on_each_item, + ); + + // add to row + payment_add(props); + + // this.setState({ + // modalVisible: false, + // paymentAmount: 0, + // }); + props.stateStore.changeValue("modalVisible", false, "Payment"); + props.stateStore.changeValue("paymentAmount", 0, "Payment"); + Toast.show({ + text: + strings.TransactionCompleted[ + strings.UnableToConnectPrinter + ], + buttonText: strings.Okay, + position: "bottom", + duration: 6000, + }); + } + + const { defaultShift } = props.shiftStore; + + // If shift started and shift hasn't ended + if (defaultShift.shiftStarted && !defaultShift.shiftEnded) { + // Set the default receipt + const { defaultReceipt } = props.receiptStore; + + // set shift + defaultReceipt.setShift(defaultShift._id); + + const { ending_cash } = defaultShift; + + // Set the end cash + defaultShift.setEndCash(ending_cash + defaultReceipt.netTotal); + } + + // props.receiptStore.defaultReceipt.clear(); + props.receiptStore.add(props.receiptStore.defaultReceipt); + props.receiptStore.setPreviousReceipt( + props.receiptStore.defaultReceipt, + ); + let discountValueForDisplay = + props.receiptStore.defaultReceipt.discounts; + let taxesValueForDisplay = props.stateStore.enableOverallTax + ? props.receiptStore.defaultReceipt.get_tax_total + : props.receiptStore.defaultReceipt + .get_tax_total_based_on_each_item; + props.receiptStore.newReceipt( + props.printerStore.companySettings[0].tax, + ); + props.receiptStore.setLastScannedBarcode(""); + props.receiptStore.unselectReceiptLine(); + props.stateStore.changeValue("selected", "Cash", "Payment"); + change_navigation( + props, + totalPurchase, + discountValueForDisplay, + taxesValueForDisplay, + ); + }, + }, + ], + ); + }); + } +} + +export function payment_add(props) { + props.paymentStore.add({ + receipt: props.receiptStore.defaultReceipt._id.toString(), + date: Date.now(), + paid: props.stateStore.settings_state[0].multipleMop + ? parseFloat(props.stateStore.payment_amount) + : parseInt(props.stateStore.payment_value, 10), + type: props.stateStore.settings_state[0].multipleMop + ? props.stateStore.payment_types + : not_multiple_payment(props), + dateUpdated: Date.now(), + syncStatus: false, + }); +} + +export function not_multiple_payment(props) { + let single_payment = []; + + single_payment.push({ + type: props.stateStore.payment_state[0].selected, + amount: parseFloat(props.stateStore.amount_due, 10), + }); + return JSON.stringify(single_payment); +} +export function change_navigation( + props, + totalPurchase, + discountValueForDisplay, + taxesValueForDisplay, +) { + props.navigation.navigate("Sales", { + cash: props.stateStore.settings_state[0].multipleMop + ? parseFloat(props.stateStore.payment_amount) + : props.stateStore.payment_value, + change: parseFloat( + (props.stateStore.settings_state[0].multipleMop + ? parseFloat(props.stateStore.payment_amount) + : parseFloat(props.stateStore.payment_value, 10)) - + (parseFloat(totalPurchase, 10) - + parseFloat(discountValueForDisplay, 10) + + parseFloat(taxesValueForDisplay, 10)), + 10, + ), + }); +} diff --git a/src/container/ReceiptInfoContainer/index.js b/src/container/ReceiptInfoContainer/index.js index 10d57bf..8a664e9 100644 --- a/src/container/ReceiptInfoContainer/index.js +++ b/src/container/ReceiptInfoContainer/index.js @@ -476,36 +476,35 @@ export default class ReceiptInfoContainer extends React.Component { ), ), ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - "\n" + - strings.POSProvider + - "Bai Web and Mobile Lab\n" + - "Insular Life Bldg, Don Apolinar\n" + - "Velez cor. Oldarico Akut St.,\n" + - "Cagayan de Oro, 9000,\n" + - "Misamis Oriental\n" + - strings.AccredNo + - strings.DateIssued + - strings.ValidUntil, - { align: "left", size: "normal" }, - true, - ), - ), - ); - writePromises.push( - BluetoothSerial.write( - TinyPOS.bufferedText( - strings.ThisReceiptShallBeValidFor + - strings.FiveYearsFromTheDateOf + - strings.ThePermitToUse, - { align: "center", size: "normal" }, - true, - ), - ), - ); - + // writePromises.push( + // BluetoothSerial.write( + // TinyPOS.bufferedText( + // "\n" + + // strings.POSProvider + + // "Bai Web and Mobile Lab\n" + + // "Insular Life Bldg, Don Apolinar\n" + + // "Velez cor. Oldarico Akut St.,\n" + + // "Cagayan de Oro, 9000,\n" + + // "Misamis Oriental\n" + + // strings.AccredNo + + // strings.DateIssued + + // strings.ValidUntil, + // { align: "left", size: "normal" }, + // true, + // ), + // ), + // ); + // writePromises.push( + // BluetoothSerial.write( + // TinyPOS.bufferedText( + // strings.ThisReceiptShallBeValidFor + + // strings.FiveYearsFromTheDateOf + + // strings.ThePermitToUse, + // { align: "center", size: "normal" }, + // true, + // ), + // ), + // ); // Add 3 new lines writePromises.push(BluetoothSerial.write(TinyPOS.bufferedLine(3))); @@ -554,8 +553,9 @@ export default class ReceiptInfoContainer extends React.Component { // payment receipt if (this.state.reasonValue) { - paymentReceipt.changeReason(this.state.reasonValue); - paymentReceipt.cancelled(paymentReceipt); + paymentReceipt.setCancel(this.state.reasonValue); + // paymentReceipt.changeReason(this.state.reasonValue); + // paymentReceipt.cancelled(paymentReceipt); this.props.shiftStore.setNewValues(obj); // Navigate to payment store diff --git a/src/container/SalesContainer/index.js b/src/container/SalesContainer/index.js index 91729e7..2dc545a 100644 --- a/src/container/SalesContainer/index.js +++ b/src/container/SalesContainer/index.js @@ -22,7 +22,8 @@ import { } from "../../utils"; import PriceModalComponent from "@components/PriceModalComponent"; -import SummaryModalComponent from "@components/SummaryModalComponent"; +import ConfirmationModalComponent from "@components/ConfirmationModalComponent"; +// import SummaryModalComponent from "@components/SummaryModalComponent"; import QuantityModalComponent from "@components/QuantityModalComponent"; import ConfirmOrderModalComponent from "@components/ConfirmOrderModalComponent"; import DiscountSelectionModalComponent from "@components/DiscountSelectionModalComponent"; @@ -40,7 +41,6 @@ import { } from "../../services/tailorder"; import { currentLanguage } from "../../translations/CurrentLanguage"; - const Sound = require("react-native-sound"); Sound.setCategory("Playback"); const beep = new Sound("beep.mp3", Sound.MAIN_BUNDLE); @@ -62,10 +62,6 @@ let strings = new LocalizedStrings(translation); @observer export default class SalesContainer extends React.Component { componentWillMount() { - const { initializeState } = this.props.stateStore; - - // Initializing the state store - initializeState(); this.getBluetoothState(); const { params } = this.props.navigation.state; @@ -82,6 +78,7 @@ export default class SalesContainer extends React.Component { "Sales", ); } + this.viewOrders("willMount"); } async getBluetoothState() { @@ -113,7 +110,10 @@ export default class SalesContainer extends React.Component { let line = ""; if (item.category !== "No Category") { const categoryObj = this.props.categoryStore.find(item.category); - line = createReceiptLine(item, categoryObj._55.name); + line = createReceiptLine( + item, + categoryObj._55 !== null ? categoryObj._55.name : "No Category", + ); } else { line = createReceiptLine(item, item.category); } @@ -223,11 +223,17 @@ export default class SalesContainer extends React.Component { onDeleteReceiptLine = () => { const { hideDeleteDialog } = this.props.stateStore; - const { unselectReceiptLine, defaultReceipt } = this.props.receiptStore; - - unselectReceiptLine(); - defaultReceipt.clear(); hideDeleteDialog(); + if (this.props.attendantStore.defaultAttendant.canApprove) { + const { unselectReceiptLine, defaultReceipt } = this.props.receiptStore; + unselectReceiptLine(); + defaultReceipt.clear(); + hideDeleteDialog(); + } else { + this.props.stateStore.changeConfirmation("AllReceiptLine"); + const { changeValue } = this.props.stateStore; + changeValue("confirmation", true, "Sales"); + } }; onBarcodeClick = () => { @@ -253,11 +259,16 @@ export default class SalesContainer extends React.Component { const { navigate } = this.props.navigation; const { defaultShift } = this.props.shiftStore; const { setAmountDue } = this.props.stateStore; + const { allowRoundOff } = this.props.stateStore.settings_state[0]; const { defaultAttendant } = this.props.attendantStore; if (defaultShift.shiftStarted && !defaultShift.shiftEnded) { if (defaultShift.attendant === defaultAttendant.user_name) { - setAmountDue(text.netTotal.toFixed(2)); + setAmountDue( + allowRoundOff + ? text.netTotalRoundOff.toString() + : text.netTotal.toFixed(2), + ); navigate("Payment", { receipt: true }); } else { showToastDanger(strings.ItIsNotYourShift); @@ -313,8 +324,17 @@ export default class SalesContainer extends React.Component { changeValue("selectedDiscount", discount, "Sales"); changeValue("selectedDiscountIndex", index, "Sales"); } - onDiscountEdit = val => { + if (this.props.attendantStore.defaultAttendant.canApprove) { + this.onDiscountApply(val); + } else { + this.props.stateStore.changeDiscountString(JSON.stringify(val)); + this.props.stateStore.changeConfirmation("AllDiscount"); + const { changeValue } = this.props.stateStore; + changeValue("confirmation", true, "Sales"); + } + }; + onDiscountApply = val => { const { changeValue } = this.props.stateStore; const { defaultReceipt } = this.props.receiptStore; const { rows, setDiscount } = this.props.discountStore; @@ -338,7 +358,6 @@ export default class SalesContainer extends React.Component { // hide modal changeValue("discountSelection", false, "Sales"); }; - confirmReceiptDeleteDialog() { const { hideDeleteDialog } = this.props.stateStore; const { deleteDialogVisible } = this.props.stateStore.sales_state[0]; @@ -372,12 +391,16 @@ export default class SalesContainer extends React.Component { onDiscountChange={(discount, index) => this.onDiscountChange(discount, index) } - selectedDiscount={this.props.stateStore.sales_state[0].selectedDiscount} + selectedDiscount={selectedDiscount ? selectedDiscount : ""} discountSelection={ - this.props.stateStore.sales_state[0].discountSelection + this.props.stateStore.sales_state.length > 0 + ? this.props.stateStore.sales_state[0].discountSelection + : "" } discountSelectionStatus={ - this.props.stateStore.sales_state[0].discountSelectionStatus + this.props.stateStore.sales_state.length > 0 + ? this.props.stateStore.sales_state[0].discountSelectionStatus + : "" } onClick={() => this.props.stateStore.changeValue("discountSelection", false, "Sales") @@ -507,55 +530,72 @@ export default class SalesContainer extends React.Component { changeValue("visibleSummaryModal", false, "Sales"); }; - summaryDialog() { - const { previousReceipt } = this.props.receiptStore; - const { cash, change } = this.props.stateStore.sales_state[0]; - const { countryCode } = this.props.printerStore.companySettings[0]; + // summaryDialog() { + // const { previousReceipt } = this.props.receiptStore; + // const { enableOverallTax } = this.props.stateStore; + // const { cash, change } = this.props.stateStore.sales_state[0]; + // const { countryCode } = this.props.printerStore.companySettings[0]; + // console.log(this.props.stateStore.sales_state[0]) + // return ( + // 0 ? cash : 0} + // change={change > 0 ? change : 0} + // onClose={this.closeSummary} + // visibility={previousReceipt ? false : false} + // lines={previousReceipt ? previousReceipt.lines.slice() : []} + // details={ + // previousReceipt && previousReceipt.lines ? previousReceipt : {} + // } + // currency={countryCode !== undefined ? countryCode : "PHP"} + // /> + // ); + // } - return ( - - ); - } onQuantitySubmit = quantity => { + if ( + this.props.attendantStore.defaultAttendant.canApprove || + parseFloat(quantity.discount) === 0 + ) { + this.setEditedFigures(quantity); + } else if ( + !this.props.attendantStore.defaultAttendant.canApprove && + parseFloat(quantity.discount) > 0 + ) { + this.props.stateStore.changeDiscountString(JSON.stringify(quantity)); + this.props.stateStore.changeConfirmation("SingleDiscount"); + const { changeValue } = this.props.stateStore; + changeValue("confirmation", true, "Sales"); + } // line + }; + setEditedFigures = figures => { this.setState({ onChangeStatues: false }); const line = this.props.receiptStore.selectedLine; - const qty = parseFloat(quantity.quantity) - ? parseFloat(quantity.quantity) - : parseFloat(quantity.defaultQty); + const qty = parseFloat(figures.quantity) + ? parseFloat(figures.quantity) + : parseFloat(figures.defaultQty); if (line.sold_by === "Each") { if (isFloat(qty)) { showToast(strings.QuantityIsNotAllowed, "warning"); } else { - showToast(strings.ReceiptLineIsModified); line.setQuantity(Number(qty.toFixed(2))); } } else { - showToast(strings.ReceiptLineIsModified); line.setQuantity(Number(qty.toFixed(2))); } - const price = parseFloat(quantity.price) - ? parseFloat(quantity.price) - : parseFloat(quantity.defaultPrice); + const price = parseFloat(figures.price) + ? parseFloat(figures.price) + : parseFloat(figures.defaultPrice); // set the price line.setPrice(Number(price.toFixed(2))); line.setDiscountRate( - parseFloat(quantity.discount) > 0 ? parseFloat(quantity.discount) : 0, - quantity.percentageType, + parseFloat(figures.discount) > 0 ? parseFloat(figures.discount) : 0, + figures.percentageType, ); // unselect the line @@ -568,10 +608,70 @@ export default class SalesContainer extends React.Component { // remove the receipt store this.props.stateStore.changeValue("quantityModalVisible", false, "Sales"); }; + execute_method = pin => { + const { changeValue } = this.props.stateStore; + this.props.attendantStore.findAttendantBasedOnRole(pin).then(result => { + if (result) { + changeValue("confirmation", false, "Sales"); + if (this.props.stateStore.currentConfirmation === "ReceiptLine") { + this.onReceiptLineDelete(this.props.stateStore.index_value); + showToast("Successfully Deleted Receiptline(s)"); + } else if ( + this.props.stateStore.currentConfirmation === "AllReceiptLine" + ) { + const { hideDeleteDialog } = this.props.stateStore; + const { + unselectReceiptLine, + defaultReceipt, + } = this.props.receiptStore; + unselectReceiptLine(); + defaultReceipt.clear(); + hideDeleteDialog(); + showToast("Successfully Deleted Receiptline(s)"); + } else if ( + this.props.stateStore.currentConfirmation === "AllDiscount" + ) { + this.onDiscountApply( + JSON.parse(this.props.stateStore.discount_string), + ); + showToast("Successfully Applied Discount"); + } else if ( + this.props.stateStore.currentConfirmation === "SingleDiscount" + ) { + this.setEditedFigures( + JSON.parse(this.props.stateStore.discount_string), + ); + showToast("Successfully Applied Discount"); + } + } else { + showToastDanger("Approvers Pin Invalid"); + } + }); + }; + confirmationModal() { + const { changeValue } = this.props.stateStore; + return ( + this.execute_method(pin)} + onClose={() => changeValue("confirmation", false, "Sales")} + /> + ); + } + showConfirmationModalReceiptLine = index => { + if (this.props.attendantStore.defaultAttendant.canApprove) { + this.onReceiptLineDelete(index); + } else { + this.props.stateStore.changeConfirmation("ReceiptLine"); + this.props.stateStore.changeIndex(index); + const { changeValue } = this.props.stateStore; + changeValue("confirmation", true, "Sales"); + } + }; onReceiptLineDelete = index => { const { queueOrigin, currentTable } = this.props.stateStore; - this.props.receiptStore.unselectReceiptLine(); const receipt = this.props.receiptStore.defaultReceipt; @@ -604,7 +704,7 @@ export default class SalesContainer extends React.Component { this.props.stateStore.changeValue("quantityModalVisible", true, "Sales"); }; - viewOrders = () => { + viewOrders = status => { const { setViewingOrder, setLoadingOrder, @@ -612,8 +712,8 @@ export default class SalesContainer extends React.Component { queueOrigin, } = this.props.stateStore; - setViewingOrder(true); - setLoadingOrder(true); + setViewingOrder(!status); + setLoadingOrder(!status); const url = `${queueOrigin}/api/v1/orders/`; @@ -652,6 +752,7 @@ export default class SalesContainer extends React.Component { setViewingOrder, } = this.props.stateStore; const { defaultReceipt } = this.props.receiptStore; + const { defaultShift } = this.props.shiftStore; const table = { id: currentTable }; showAlert( @@ -662,6 +763,7 @@ export default class SalesContainer extends React.Component { setCurrentTable(-1); defaultReceipt.clear(); setViewingOrder(false); + defaultShift.orderVoid(); showToast(`${strings.Order} ${res.table_no} ${strings.IsCancelled}`); }); }, @@ -766,7 +868,6 @@ export default class SalesContainer extends React.Component { tableNo = null; } const order = getOrder(orderType, items, tableNo); - sendOrder(queueOrigin, order) .then(res => { unselectReceiptLine(); @@ -853,19 +954,22 @@ export default class SalesContainer extends React.Component { return ( {this.discountSelectionDialog()} - {this.summaryDialog()} + {/*{this.summaryDialog()}*/} {this.confirmReceiptDeleteDialog()} {this.quantityEditDialog()} {this.priceInputDialog()} {this.onConfirmOrderDialog()} + {this.confirmationModal()} 0 @@ -907,7 +1011,7 @@ export default class SalesContainer extends React.Component { // receipt line onPaymentClick={this.onPaymentClick} onReceiptLineEdit={this.onReceiptLineEdit} - onReceiptLineDelete={this.onReceiptLineDelete} + onReceiptLineDelete={this.showConfirmationModalReceiptLine} // empty rows onEndReached={this.onEndReached} onLongPressItem={this.onLongPressItem} @@ -935,6 +1039,7 @@ export default class SalesContainer extends React.Component { onChangeTable={this.onChangeTable} onReprintOrder={this.onReprintOrder} isCurrencyDisabled={this.props.stateStore.isCurrencyDisabled} + enableOverallTax={this.props.stateStore.enableOverallTax} /> ); diff --git a/src/container/SettingsContainer/constants.json b/src/container/SettingsContainer/constants.json index 4267554..baaf5a4 100644 --- a/src/container/SettingsContainer/constants.json +++ b/src/container/SettingsContainer/constants.json @@ -120,5 +120,6 @@ {"name":"XOF"}, {"name":"XPF"}, {"name":"ZAR"}, - {"name":"ZMW"} + {"name":"ZMW"}, + {"name":"MWK"} ] \ No newline at end of file diff --git a/src/container/SettingsContainer/index.js b/src/container/SettingsContainer/index.js index 6dec2ae..505758a 100644 --- a/src/container/SettingsContainer/index.js +++ b/src/container/SettingsContainer/index.js @@ -86,22 +86,51 @@ export default class SettingsContainer extends React.Component { ); } if (this.props.printerStore.companySettings.length > 0) { - // Alert.alert("", this.props.printerStore.companySettings[0].name + " " + this.props.printerStore.companySettings[0].header + " " + this.props.printerStore.companySettings[0].footer) - // this.setState({ - // companyName: this.props.printerStore.companySettings[0].name.toString(), - // companyHeader: this.props.printerStore.companySettings[0].header.toString(), - // companyFooter: this.props.printerStore.companySettings[0].footer.toString(), - // }); this.props.stateStore.changeValue( "companyName", this.props.printerStore.companySettings[0].name.toString(), "Settings", ); + this.props.stateStore.changeValue( + "smallSizeIcon", + this.props.printerStore.companySettings[0].smallSizeIcon, + "Settings", + ); + this.props.stateStore.changeValue( + "mediumSizeIcon", + this.props.printerStore.companySettings[0].mediumSizeIcon, + "Settings", + ); + this.props.stateStore.changeValue( + "largeSizeIcon", + this.props.printerStore.companySettings[0].largeSizeIcon, + "Settings", + ); + this.props.stateStore.changeValue( + "multipleMop", + this.props.printerStore.companySettings[0].multipleMop, + "Settings", + ); + this.props.stateStore.changeValue( + "allowRoundOff", + this.props.printerStore.companySettings[0].allowRoundOff, + "Settings", + ); + this.props.stateStore.changeValue( + "hideMenuBar", + this.props.printerStore.companySettings[0].hideMenuBar, + "Settings", + ); this.props.stateStore.changeValue( "tax", this.props.printerStore.companySettings[0].tax.toString(), "Settings", ); + this.props.stateStore.changeValue( + "changeNoReceipts", + this.props.printerStore.companySettings[0].changeNoReceipts.toString(), + "Settings", + ); this.props.stateStore.changeValue( "companyHeader", this.props.printerStore.companySettings[0].header.toString(), @@ -130,6 +159,9 @@ export default class SettingsContainer extends React.Component { this.props.stateStore.changeCompanyCheckBox( this.props.printerStore.companySettings[0].currencyDisable, ); + this.props.stateStore.changeOverallTax( + this.props.printerStore.companySettings[0].enableOverallTax, + ); } for (let i = 0; i < this.props.printerStore.rows.length; i += 1) { if (this.props.printerStore.rows[i].defaultPrinter) { @@ -153,7 +185,6 @@ export default class SettingsContainer extends React.Component { this.props.printerStore.rows[i]._id, "Settings", ); - BluetoothSerial.connect(this.props.printerStore.rows[i].macAddress) .then(() => { // this.setState({ @@ -189,6 +220,7 @@ export default class SettingsContainer extends React.Component { BluetoothStatus.enable(true); } } + onButtonPress = value => { this.props.printerStore.addFoundDevices(value); }; @@ -371,7 +403,10 @@ export default class SettingsContainer extends React.Component { header: this.props.stateStore.settings_state[0].companyHeader, footer: this.props.stateStore.settings_state[0].companyFooter, countryCode: this.props.stateStore.settings_state[0].companyCountry, + enableOverallTax: this.props.stateStore.enableOverallTax, currencyDisable: this.props.stateStore.isCurrencyDisabled, + changeNoReceipts: this.props.stateStore.settings_state[0] + .changeNoReceipts, }); } else { this.props.printerStore.addCompany({ @@ -383,6 +418,9 @@ export default class SettingsContainer extends React.Component { footer: this.props.stateStore.settings_state[0].companyFooter, countryCode: this.props.stateStore.settings_state[0].companyCountry, currencyDisable: this.props.stateStore.isCurrencyDisabled, + enableOverallTax: this.props.stateStore.enableOverallTax, + changeNoReceipts: this.props.stateStore.settings_state[0] + .changeNoReceipts, }); } @@ -409,6 +447,14 @@ export default class SettingsContainer extends React.Component { }); } }; + onSaveNoReceipts = receiptNo => { + const { stateStore, printerStore } = this.props; + stateStore.changeValue("changeNoReceipts", receiptNo, "Settings"); + let company = printerStore.findCompany(printerStore.companySettings[0]._id); + company.edit({ + changeNoReceipts: receiptNo, + }); + }; bluetoothScannerStatus(text) { if (this.props.printerStore.bluetooth.length > 0) { let bluetoothScanner = this.props.printerStore.findBluetoothScanner( @@ -477,6 +523,7 @@ export default class SettingsContainer extends React.Component { pin_code: values.pin, role: values.role, canLogin: values.canLogin, + canApprove: values.canApprove, commission: parseInt(values.commission, 10) > 0 ? parseInt(values.commission, 10) @@ -500,9 +547,6 @@ export default class SettingsContainer extends React.Component { }); } }); - - // this.props.stateStore.changeValue("attendants", JSON.stringify(this.props.attendantStore.rows.slice()), "Settings") - // this.props.stateStore.changeValue("attendantsInfo",{}, "Settings") } else if (values.status === "Edit Attendant") { const valueAttendant = await this.props.attendantStore.find( values.id, @@ -514,6 +558,7 @@ export default class SettingsContainer extends React.Component { pin_code: values.pin, role: values.role, canLogin: values.canLogin, + canApprove: values.canApprove, commission: parseInt(values.commission, 10) > 0 ? parseInt(values.commission, 10) @@ -595,6 +640,7 @@ export default class SettingsContainer extends React.Component { pin_code: values.pin, role: values.role, canLogin: values.canLogin, + canApprove: values.canApprove, commission: parseInt(values.commission, 10) > 0 ? parseInt(values.commission, 10) @@ -632,6 +678,7 @@ export default class SettingsContainer extends React.Component { pin_code: values.pin, role: values.role, canLogin: values.canLogin, + canApprove: values.canApprove, commission: parseInt(values.commission, 10) > 0 ? parseInt(values.commission, 10) @@ -817,6 +864,62 @@ export default class SettingsContainer extends React.Component { ], ); }; + toggleItemSize = size => { + const { stateStore } = this.props; + stateStore.changeValue("smallSizeIcon", size === "Small", "Settings"); + stateStore.changeValue("mediumSizeIcon", size === "Medium", "Settings"); + stateStore.changeValue("largeSizeIcon", size === "Large", "Settings"); + + if (this.props.printerStore.companySettings.length > 0) { + let company = this.props.printerStore.findCompany( + this.props.printerStore.companySettings[0]._id, + ); + company.edit({ + smallSizeIcon: size === "Small", + mediumSizeIcon: size === "Medium", + largeSizeIcon: size === "Large", + }); + } + }; + toggleMultipleMop = () => { + const { stateStore } = this.props; + const { multipleMop } = stateStore.settings_state[0]; + stateStore.changeValue("multipleMop", !multipleMop, "Settings"); + if (this.props.printerStore.companySettings.length > 0) { + let company = this.props.printerStore.findCompany( + this.props.printerStore.companySettings[0]._id, + ); + company.edit({ + multipleMop: !multipleMop, + }); + } + }; + toggleAllowRoundOff = () => { + const { stateStore } = this.props; + const { allowRoundOff } = stateStore.settings_state[0]; + stateStore.changeValue("allowRoundOff", !allowRoundOff, "Settings"); + if (this.props.printerStore.companySettings.length > 0) { + let company = this.props.printerStore.findCompany( + this.props.printerStore.companySettings[0]._id, + ); + company.edit({ + allowRoundOff: !allowRoundOff, + }); + } + }; + toggleHideMenuBar = () => { + const { stateStore } = this.props; + const { hideMenuBar } = stateStore.settings_state[0]; + stateStore.changeValue("hideMenuBar", !hideMenuBar, "Settings"); + if (this.props.printerStore.companySettings.length > 0) { + let company = this.props.printerStore.findCompany( + this.props.printerStore.companySettings[0]._id, + ); + company.edit({ + hideMenuBar: !hideMenuBar, + }); + } + }; render() { strings.setLanguage(currentLanguage().companyLanguage); const { @@ -845,6 +948,10 @@ export default class SettingsContainer extends React.Component { availableDevicesChangeValue={text => stateStore.changeValue("availableDevices", text, "Settings") } + toggleItemSize={size => this.toggleItemSize(size)} + toggleMultipleMop={size => this.toggleMultipleMop(size)} + toggleAllowRoundOff={this.toggleAllowRoundOff} + toggleHideMenuBar={this.toggleHideMenuBar} checkBoxValueOnChange={this.onCheckBoxValueOnChange} bluetoothScannerStatus={text => { stateStore.changeValue("checkBoxBluetoothValue", text, "Settings"); @@ -859,6 +966,7 @@ export default class SettingsContainer extends React.Component { changeName={text => stateStore.changeValue("companyName", text, "Settings") } + changeNoReceipts={this.onSaveNoReceipts} changeTax={text => stateStore.changeValue("tax", text, "Settings")} changeCountry={text => this.onChangeCurrency(text)} changeHeader={text => @@ -915,7 +1023,9 @@ export default class SettingsContainer extends React.Component { toggleTailOrder={stateStore.toggleTailOrder} onQueueSave={this.onQueueSave} toggleCurrencyDisabled={this.props.stateStore.toggleCurrencyDisabled} + toggleEnableOverallTax={this.props.stateStore.toggleEnableOverallTax} isCurrencyDisabled={stateStore.isCurrencyDisabled} + enableOverallTax={stateStore.enableOverallTax} // Queue Settings isEditingQueue={stateStore.isEditingQueue} setQueueEditing={stateStore.setQueueEditing} diff --git a/src/store/PosStore/AttendantStore.js b/src/store/PosStore/AttendantStore.js index 42b0fbc..68b57f3 100644 --- a/src/store/PosStore/AttendantStore.js +++ b/src/store/PosStore/AttendantStore.js @@ -19,6 +19,7 @@ export const Attendant = types user_name: types.string, pin_code: types.string, canLogin: types.optional(types.boolean, false), + canApprove: types.optional(types.boolean, false), role: types.optional(types.string, ""), dateUpdated: types.optional(types.Date, Date.now), syncStatus: types.optional(types.boolean, false), @@ -138,6 +139,7 @@ const AttendantStore = types pin_code: doc.pin_code, role: doc.role, canLogin: doc.canLogin ? doc.canLogin : false, + canApprove: doc.canApprove ? doc.canApprove : false, commission: doc.commission, dateUpdated: doc.dateUpdated, syncStatus: doc.syncStatus, @@ -180,6 +182,24 @@ const AttendantStore = types }); }); }, + findAttendantBasedOnRole(pin) { + return new Promise(function(resolve, reject) { + db + .find({ + selector: { + canApprove: { $regex: true }, + pin_code: { $regex: pin }, + }, + }) + .then(result => { + if (result.docs.length > 0) { + resolve(true); + } else { + resolve(false); + } + }); + }); + }, })); const Store = AttendantStore.create({}); diff --git a/src/store/PosStore/DbFunctions.js b/src/store/PosStore/DbFunctions.js index 5d6a011..2add6ee 100644 --- a/src/store/PosStore/DbFunctions.js +++ b/src/store/PosStore/DbFunctions.js @@ -2,9 +2,10 @@ import PouchDB from "pouchdb-react-native"; import SQLite from "react-native-sqlite-2"; import SQLiteAdapterFactory from "pouchdb-adapter-react-native-sqlite"; import { getRoot } from "mobx-state-tree"; -import { unformat } from "accounting-js"; import FrappeFetch from "react-native-frappe-fetch"; -import { Toast } from "native-base"; +import { NetInfo } from "react-native"; +import { showToastDanger } from "../../utils"; + var validUrl = require("valid-url"); import BackgroundJob from "react-native-background-job"; @@ -16,7 +17,6 @@ export function openAndSyncDB(dbName, withSync = false) { PouchDB.plugin(require("pouchdb-upsert")); return db; } - export function syncDB(db, dbName, session) { // Server URL const url = `https://${session.db_name}:${session.token}@db.tailpos.com/${ @@ -37,82 +37,77 @@ export function sync( jobStatus, store, ) { - // if (credentials.url !== undefined && credentials.user_name !== undefined && credentials.password !== undefined) { - if (credentials.url) { - if (validUrl.isWebUri(credentials.url.toLowerCase())) { - return FrappeFetch.createClient({ - url: credentials.url.toLowerCase(), - username: credentials.user_name, - password: credentials.password, - }) + return NetInfo.isConnected.fetch().then(async isConnected => { + if (isConnected) { + let site_credentials = credentials_info(credentials); + let tailpos_object = tailpos_data( + jsonObject, + type, + trashObj, + credentials, + ); + return sync_now(site_credentials, tailpos_object, jobStatus, store); + } else { + showToastDanger("No Internet Connection. Please Check"); + } + }); +} - .then(responseLog => { +export function sync_now(site_credentials, tailpos_object, jobStatus, store) { + if (site_credentials.url) { + if (validUrl.isWebUri(site_credentials.url)) { + return FrappeFetch.createClient(site_credentials) + .then(() => { const { Client } = FrappeFetch; return Client.postApi( "tailpos_sync.sync_pos.sync_data", - // "frappe.handler.ping", - { - tailposData: JSON.parse(jsonObject), - trashObject: JSON.parse(trashObj), - deviceId: credentials.deviceId, - typeOfSync: type, - }, + tailpos_object, ); }) - .catch(error => { + .catch(() => { store.stateStore.setIsNotSyncing(); BackgroundJob.cancel({ jobKey: "AutomaticSync" }); if (!jobStatus) { - Toast.show({ - text: "Unable to sync", - type: "danger", - duration: 5000, - }); + showToastDanger( + "Unable to sync. Please check error logs in ERPNext", + ); } }) - .then(response => response.json()) .then(responseJson => { - return responseJson.message.data; + if (responseJson.message.status) { + return responseJson.message.data; + } else { + showToastDanger( + "Unable to sync. Please check error logs in ERPNext", + ); + } }); } else { store.stateStore.setIsNotSyncing(); - Toast.show({ - text: "Invalid URL", - type: "danger", - duration: 5000, - }); + showToastDanger("Invalid URL. Please input valid URL in Sync Settings"); BackgroundJob.cancel({ jobKey: "AutomaticSync" }); } } else { BackgroundJob.cancel({ jobKey: "AutomaticSync" }); } } - -export function changeItemsStatusValue(table) { - table.allDocs({ include_docs: true }).then(entries => { - if (entries && entries.rows.length > 0) { - for (var i = 0; i < entries.rows.length; i++) { - if (entries.rows[i].doc.name) { - const entry = entries.rows[i].doc; - const objectValue = { - name: entry.name, - soldBy: entry.soldBy, - price: unformat(entry.price), - sku: entry.sku, - barcode: entry.barcode, - category: entry.category, - colorAndShape: JSON.stringify(entry.colorAndShape), - taxes: JSON.stringify(entry.taxes), - dateUpdated: entry.dateUpdated, - syncStatus: true, - }; - editFields(entry, objectValue); - } - } - } - }); +export function credentials_info(credentials) { + return { + url: credentials.url.toLowerCase(), + username: credentials.user_name, + password: credentials.password, + }; } +export function tailpos_data(jsonObject, type, trashObj, credentials) { + return { + tailposData: JSON.parse(jsonObject), + trashObject: JSON.parse(trashObj), + deviceId: credentials.deviceId, + typeOfSync: type, + }; +} + export function saveSnapshotToDB(db, snapshot) { let updateObj = false; db.upsert(snapshot._id, function(doc) { @@ -162,8 +157,6 @@ export function getRows(obj, db, numberRows, rowsOptions) { rowsOptions.skip = 1; for (var i = 0; i < entries.rows.length; i++) { if (entries.rows[i].doc.name || entries.rows[i].doc.role) { - // entries.rows[i].doc.dateUpdated = Date.now(); - // entries.rows[i].doc.syncStatus = false; obj.add(JSON.parse(JSON.stringify(entries.rows[i].doc))); } } diff --git a/src/store/PosStore/ItemStore.js b/src/store/PosStore/ItemStore.js index 67c219a..bee3e31 100644 --- a/src/store/PosStore/ItemStore.js +++ b/src/store/PosStore/ItemStore.js @@ -24,6 +24,7 @@ export const Item = types description: types.optional(types.string, ""), soldBy: types.string, price: types.optional(types.number, 0), + tax: types.optional(types.number, 0), sku: types.optional(types.string, ""), barcode: types.union(types.string, types.number), colorAndShape: types.optional(types.string, ""), @@ -155,6 +156,11 @@ const Store = types } } }, + resetLengths(obj) { + let objectLength = JSON.parse(self.categoryLengths); + objectLength = []; + self.categoryLengths = JSON.stringify(objectLength); + }, updateLengthObjects(obj) { if (obj) { let objectLength = JSON.parse(self.categoryLengths); diff --git a/src/store/PosStore/PaymentStore.js b/src/store/PosStore/PaymentStore.js index 93802fd..4345e5e 100644 --- a/src/store/PosStore/PaymentStore.js +++ b/src/store/PosStore/PaymentStore.js @@ -21,7 +21,7 @@ export const Payment = types date: types.Date, receipt: types.string, paid: types.number, - type: types.enumeration("Type", ["Cash", "Card", "Visa", "Amex", "Sapn"]), + type: types.optional(types.string, ""), deviceId: types.optional(types.string, DeviceInfo.getDeviceId()), dateUpdated: types.optional(types.Date, Date.now), syncStatus: types.optional(types.boolean, false), @@ -52,6 +52,19 @@ const PaymentStore = types const paid = self.defaultPayment.paid; return paid - netTotal; }, + get amountChangeRoundOff() { + const netTotal = + parseFloat(self.paymentReceipt.netTotal, 10) - + parseInt(self.paymentReceipt.netTotal, 10); + + const paid = self.defaultPayment.paid; + return ( + paid - + (netTotal < 0.5 + ? parseInt(self.paymentReceipt.netTotal, 10) + : parseFloat(self.paymentReceipt.netTotal, 10)) + ); + }, })) .actions(self => ({ initSync(session) { diff --git a/src/store/PosStore/PrinterStore.js b/src/store/PosStore/PrinterStore.js index 5ba5147..3627822 100644 --- a/src/store/PosStore/PrinterStore.js +++ b/src/store/PosStore/PrinterStore.js @@ -72,11 +72,19 @@ export const Company = types name: types.string, header: types.string, footer: types.string, + changeNoReceipts: types.string, companyLanguage: types.string, tax: types.optional(types.string, "0"), countryCode: types.optional(types.string, "PHP"), currencyDisable: types.optional(types.boolean, false), + enableOverallTax: types.optional(types.boolean, false), + smallSizeIcon: types.optional(types.boolean, false), + mediumSizeIcon: types.optional(types.boolean, false), + largeSizeIcon: types.optional(types.boolean, true), hideCategory: types.optional(types.boolean, false), + multipleMop: types.optional(types.boolean, false), + hideMenuBar: types.optional(types.boolean, false), + allowRoundOff: types.optional(types.boolean, false), }) .preProcessSnapshot(snapshot => assignUUID(snapshot, "Company")) .actions(self => ({ @@ -333,29 +341,60 @@ const Store = types if (!entries.rows[i].doc.companyLanguage) { entries.rows[i].doc.companyLanguage = "en"; } + if (!entries.rows[i].doc.changeNoReceipts) { + entries.rows[i].doc.changeNoReceipts = "1"; + } + if (!entries.rows[i].doc.smallSizeIcon) { + entries.rows[i].doc.smallSizeIcon = false; + } + if (!entries.rows[i].doc.mediumSizeIcon) { + entries.rows[i].doc.mediumSizeIcon = false; + } + if (!entries.rows[i].doc.largeSizeIcon) { + entries.rows[i].doc.largeSizeIcon = false; + } + if (!entries.rows[i].doc.multipleMop) { + entries.rows[i].doc.multipleMop = false; + } + if (!entries.rows[i].doc.allowRoundOff) { + entries.rows[i].doc.allowRoundOff = false; + } + self.addCompany(JSON.parse(JSON.stringify(entries.rows[i].doc))); } } if (entries.rows.length <= 0) { self.addCompany({ name: "", + changeNoReceipts: "1", header: "", footer: "", companyLanguage: "en", tax: "0", countryCode: "PHP", currencyDisable: false, + smallSizeIcon: false, + mediumSizeIcon: false, + largeSizeIcon: true, + multipleMop: false, + allowRoundOff: false, }); } } else { self.addCompany({ name: "", + changeNoReceipts: "1", header: "", companyLanguage: "en", footer: "", tax: "0", countryCode: "PHP", currencyDisable: false, + smallSizeIcon: false, + mediumSizeIcon: false, + largeSizeIcon: true, + multipleMop: false, + allowRoundOff: false, }); } }); diff --git a/src/store/PosStore/ReceiptStore.js b/src/store/PosStore/ReceiptStore.js index d7e9796..231f279 100644 --- a/src/store/PosStore/ReceiptStore.js +++ b/src/store/PosStore/ReceiptStore.js @@ -26,6 +26,7 @@ export const ReceiptLine = types qty: types.number, commission_details: types.optional(types.string, "[]"), discount_rate: types.optional(types.number, 0), + tax: types.optional(types.number, 0), discountType: types.optional(types.string, "percentage"), }) .preProcessSnapshot(snapshot => assignUUID(snapshot, "ReceiptLine")) @@ -43,6 +44,9 @@ export const ReceiptLine = types } return self.price * self.qty; }, + get tax_total() { + return self.total * (self.tax / 100); + }, })) .actions(self => ({ setQuantity(qty) { @@ -98,11 +102,13 @@ export const Receipt = types receiptNumber: types.optional(types.number, 0), discountType: types.optional(types.string, "percentage"), taxesValue: types.optional(types.string, ""), + taxesAmount: types.optional(types.number, 0), shift: types.optional(types.string, ""), deviceId: types.optional(types.string, DeviceInfo.getDeviceId()), dateUpdated: types.optional(types.Date, Date.now), syncStatus: types.optional(types.boolean, false), + roundOff: types.optional(types.boolean, false), attendant: types.optional(types.string, ""), orderType: types.optional( types.enumeration("OrderType", [ @@ -164,8 +170,26 @@ export const Receipt = types if (netTotal <= 0) { netTotal = 0; } + return netTotal; }, + get netTotalRoundOff() { + let discountValue = self.discountValue; + if (self.discountType === "percentage") { + discountValue = discountValue * self.grandTotal; + } + + let netTotal = self.grandTotal - discountValue; + + if (netTotal <= 0) { + netTotal = 0; + } + let roundoff_check = + parseFloat(netTotal, 10).toFixed(2) - parseInt(netTotal, 10); + return roundoff_check < 0.5 + ? parseInt(netTotal, 10) + : parseFloat(netTotal, 10).toFixed(2); + }, get grandQuantity() { if (self.lines.length !== 0) { let total = 0; @@ -187,6 +211,20 @@ export const Receipt = types } return 0; }, + get subtotalRoundOff() { + if (self.lines.length !== 0) { + let total = 0; + for (let i = 0; i < self.lines.length; i++) { + total = total + self.lines[i].total; + } + let roundoff_check = + parseFloat(total, 10).toFixed(2) - parseInt(total, 10); + return roundoff_check < 0.5 + ? parseInt(total, 10) + : parseFloat(total, 10).toFixed(2); + } + return 0; + }, get get_tax_total() { if (self.lines.length !== 0) { let total = 0; @@ -198,6 +236,17 @@ export const Receipt = types } return 0; }, + get get_tax_total_based_on_each_item() { + if (self.lines.length !== 0) { + let total = 0; + for (let i = 0; i < self.lines.length; i++) { + total = total + self.lines[i].tax_total; + } + + return total; + } + return 0; + }, get linesLength() { return self.lines.length; }, @@ -255,6 +304,9 @@ export const Receipt = types setOrderType(orderType) { self.orderType = orderType; }, + setRoundOff(roundOff) { + self.roundOff = roundOff; + }, add(line, isStackItem) { let resLine = null; @@ -341,9 +393,10 @@ export const Receipt = types // Yay! self.lines.splice(0, self.lines.length); }, - completed(attendant) { + setAttendant(attendant) { self.attendant = attendant; - + }, + completed(attendant) { self.status = "completed"; }, cancelled(obj) { @@ -358,6 +411,12 @@ export const Receipt = types // self.dateUpdate = Date.now; self.syncStatus = true; }, + setCancel(reason) { + self.reason = reason; + self.status = "cancelled"; + self.dateUpdated = Date.now(); + self.syncStatus = false; + }, })); const Store = types @@ -449,6 +508,7 @@ const Store = types setReceipt(receipt) { self.defaultReceipt = receipt; }, + async setDefaultCustomer() { return await customerDB .find({ @@ -538,7 +598,7 @@ const Store = types }, currentReceipt(tax) { - if (!self.defaultReceipt || self.defaultReceipt.status === "completed") { + if (!self.defaultReceipt) { db .find({ selector: { @@ -630,7 +690,6 @@ const Store = types if (result && result.docs.length > 0) { for (let x = 0; x < result.docs.length; x++) { const doc = result.docs[x]; - const receiptObj = Receipt.create({ _id: doc._id, date: Date.parse(new Date(doc.date).toDateString()), diff --git a/src/store/PosStore/ShiftStore.js b/src/store/PosStore/ShiftStore.js index 2371986..25d92bb 100644 --- a/src/store/PosStore/ShiftStore.js +++ b/src/store/PosStore/ShiftStore.js @@ -49,6 +49,8 @@ export const Shift = types syncStatus: types.optional(types.boolean, false), categories_total_amounts: types.optional(types.string, "[]"), mop_total_amounts: types.optional(types.string, "[]"), + voided: types.optional(types.number, 0), + cancelled: types.optional(types.number, 0), }) .preProcessSnapshot(snapshot => assignUUID(snapshot, "Shift")) .views(self => ({ @@ -193,6 +195,13 @@ export const Shift = types changeShort() { self.short = 0; }, + receiptCancelled(cancelled) { + // self.voided = self.voided + 1; + self.cancelled = self.cancelled + parseFloat(cancelled); + }, + orderVoid() { + self.voided = self.voided + 1; + }, changeValues(obj) { self.beginning_cash += obj.beginning_cash; self.ending_cash += obj.ending_cash; @@ -295,6 +304,7 @@ const ShiftStore = types }, setNewValues(obj) { self.defaultShift.minusValues(obj); + self.defaultShift.receiptCancelled(obj.total); }, findShift(id) { return new Promise(function(resolve, reject) { @@ -373,6 +383,8 @@ const ShiftStore = types mop_total_amounts: doc.mop_total_amounts, dateUpdated: Date.now(), syncStatus: false, + voided: doc.voided, + cancelled: doc.cancelled, }); Object.keys(doc.pays).map(key => { @@ -407,6 +419,8 @@ const ShiftStore = types mop_total_amounts: entries.rows[i].doc.mop_total_amounts, dateUpdated: Date.now(), syncStatus: false, + voided: entries.rows[i].doc.voided, + cancelled: entries.rows[i].doc.cancelled, }); Object.keys(entries.rows[i].doc.pays).map(key => { zReadingObj.addPay(entries.rows[i].doc.pays[key]); @@ -451,7 +465,8 @@ const ShiftStore = types status: report.status, categories_total_amounts: report.categories_total_amounts, mop_total_amounts: report.mop_total_amounts, - + voided: report.voided, + cancelled: report.cancelled, commissions: report.commissions, reportType: report.reportType, dateUpdated: report.dateUpdated, diff --git a/src/store/PosStore/SyncStore.js b/src/store/PosStore/SyncStore.js index 6d99a5f..c42bd05 100644 --- a/src/store/PosStore/SyncStore.js +++ b/src/store/PosStore/SyncStore.js @@ -1,14 +1,7 @@ import { types } from "mobx-state-tree"; import { openAndSyncDB, sync, saveSnapshotToDB } from "./DbFunctions"; import { assignUUID } from "./Utils"; -// let Item = openAndSyncDB("items"); -// let Category = openAndSyncDB("categories"); -// let Discount = openAndSyncDB("discounts"); -// let shiftDb = openAndSyncDB("categories"); -// let attendantDb = openAndSyncDB("categories"); -// let shiftReportDb = openAndSyncDB("categories"); -// let receiptDb = openAndSyncDB("categories"); -// let paymentDb = openAndSyncDB("categories"); + let trash = openAndSyncDB("trash", true); export const Trash = types .model("Trash", { diff --git a/src/store/PosStore/syncInBackground.js b/src/store/PosStore/syncInBackground.js index a51909a..7aa37ef 100644 --- a/src/store/PosStore/syncInBackground.js +++ b/src/store/PosStore/syncInBackground.js @@ -7,7 +7,7 @@ import BackgroundJob from "react-native-background-job"; export function syncObjectValues(status, store, jobStatus) { strings.setLanguage(currentLanguage().companyLanguage); - + store.itemStore.resetLengths(); const { forceSync, selectedSync } = store.syncStore; let syncStoreMethod = ""; @@ -28,9 +28,16 @@ export function syncObjectValues(status, store, jobStatus) { const syncInfo = { deviceId: store.stateStore.deviceId, - url: protocol + store.printerStore.sync[0].url, - user_name: store.printerStore.sync[0].user_name, - password: store.printerStore.sync[0].password, + url: + store.printerStore.sync[0].url !== undefined + ? protocol + store.printerStore.sync[0].url + : "", + user_name: store.printerStore.sync[0].user_name + ? store.printerStore.sync[0].user_name + : "", + password: store.printerStore.sync[0].password + ? store.printerStore.sync[0].password + : "", }; store.syncStore @@ -53,6 +60,8 @@ export function syncObjectValues(status, store, jobStatus) { await attendantSync(data[x], store); } else if (table === "Company") { await companySync(data[x], store); + } else if (table === "Wallet") { + await walletSync(data[x], store); } } @@ -69,27 +78,30 @@ export function syncObjectValues(status, store, jobStatus) { } } await changeSyncStatusValue(result, store); - + await category_lengths(store); if (!jobStatus) { - Toast.show({ - text: strings.SyncSuccessful, - duration: 3000, - }); - store.stateStore.setIsNotSyncing(); + setTimeout(() => { + Toast.show({ + text: strings.SyncSuccessful + ". Please restart TailPOS", + duration: 30000, + }); + store.stateStore.setIsNotSyncing(); + }, 10000); } BackgroundJob.cancel({ jobKey: "AutomaticSync" }); }); } else { if (!jobStatus) { - Toast.show({ - text: strings.AlreadyUpToDate, - type: "danger", - duration: 3000, - }); - store.stateStore.setIsNotSyncing(); + setTimeout(() => { + Toast.show({ + text: strings.AlreadyUpToDate, + type: "danger", + duration: 3000, + }); + store.stateStore.setIsNotSyncing(); + }, 10000); } - BackgroundJob.cancel({ jobKey: "AutomaticSync" }); } }); @@ -104,7 +116,6 @@ export async function itemSync(itemObject, store) { ); if (categoryIds) { categoryId = categoryIds._id; - store.itemStore.updateLengthObjects(categoryIds._id); } } else { categoryId = "No Category"; @@ -123,6 +134,10 @@ export async function itemSync(itemObject, store) { itemObject.syncObject.standard_rate !== null ? itemObject.syncObject.standard_rate : 0, + tax: + itemObject.syncObject.tax_rate !== null + ? itemObject.syncObject.tax_rate + : 0, sku: itemObject.syncObject.sku !== null ? itemObject.syncObject.sku : "", barcode: itemObject.syncObject.barcode === null || @@ -159,6 +174,7 @@ export async function itemSync(itemObject, store) { }); } else { var objecct_to_add = { + _id: itemObject.syncObject.id, name: itemObject.syncObject.name !== null ? itemObject.syncObject.name : "", description: @@ -175,6 +191,10 @@ export async function itemSync(itemObject, store) { itemObject.syncObject.standard_rate !== null ? itemObject.syncObject.standard_rate : 0, + tax: + itemObject.syncObject.tax_rate !== null + ? itemObject.syncObject.tax_rate + : 0, sku: itemObject.syncObject.sku !== null ? itemObject.syncObject.sku : "", barcode: itemObject.syncObject.barcode !== null && @@ -206,20 +226,13 @@ export async function itemSync(itemObject, store) { category: categoryId, taxes: "[]", dateUpdated: Date.now(), - syncStatus: itemObject.syncObject.id !== null ? true : false, + syncStatus: itemObject.syncObject.id !== null, }; itemObject.syncObject.id !== null ? (objecct_to_add._id = itemObject.syncObject.id) : null; store.itemStore.add(objecct_to_add); - itemObject.syncObject.category !== null - ? store.itemStore.updateLengthObjects(itemObject.syncObject.category) - : null; - itemObject.syncObject.category !== null || - itemObject.syncObject.category === null - ? store.itemStore.updateLength() - : null; } } @@ -405,7 +418,47 @@ export async function customerSync(customerObject, store) { } } } +export async function walletSync(walletObject, store) { + if (walletObject.syncObject.id !== null) { + const walletObjectResult = await store.walletStore.find( + walletObject.syncObject.id, + ); + if (walletObjectResult) { + walletObjectResult.edit({ + _id: walletObjectResult.syncObject.id, + wallet_card_number: + walletObjectResult.syncObject.wallet_card_number !== null + ? walletObjectResult.syncObject.wallet_card_number + : "", + prepaid_balance: + walletObjectResult.syncObject.prepaid_balance !== null + ? walletObjectResult.syncObject.prepaid_balance + : 0, + credit_limit: + walletObjectResult.syncObject.credit_limit !== null + ? walletObjectResult.syncObject.credit_limit + : 0, + }); + } else { + store.walletStore.add({ + _id: walletObject.syncObject.id, + wallet_card_number: + walletObject.syncObject.wallet_card_number !== null + ? walletObject.syncObject.wallet_card_number + : "", + prepaid_balance: + walletObject.syncObject.prepaid_balance !== null + ? walletObject.syncObject.prepaid_balance + : 0, + credit_limit: + walletObject.syncObject.credit_limit !== null + ? walletObject.syncObject.credit_limit + : 0, + }); + } + } +} export async function companySync(companyObject, store) { const companyObjectResult = await store.printerStore.findCompany( store.printerStore.companySettings[0]._id, @@ -549,3 +602,6 @@ export async function deleteRecords(deletedObject, store) { } } } +export async function category_lengths(props) { + await props.itemStore.getLengthItemsFromDb(); +} diff --git a/src/store/StateStore/DefaultValues.js b/src/store/StateStore/DefaultValues.js index ec4f836..03c9ec9 100644 --- a/src/store/StateStore/DefaultValues.js +++ b/src/store/StateStore/DefaultValues.js @@ -22,6 +22,7 @@ export const sales = { fetching: false, addReceiptLineStatus: false, confirmOrder: false, + confirmation: false, commissionArray: JSON.stringify([]), }; export const listing = { @@ -54,6 +55,7 @@ export const payment = { customerName: "", customerEmail: "", customerPhoneNumber: "", + walletCardNumber: "", customerNotes: "", arrayObjects: JSON.stringify([]), }; @@ -63,6 +65,13 @@ export const settings = { connectionStatus: "Not Connected", currentAddress: "", companyName: "", + changeNoReceipts: "1", + smallSizeIcon: false, + mediumSizeIcon: false, + largeSizeIcon: true, + multipleMop: false, + allowRoundOff: false, + hideMenuBar: false, tax: "0", companyHeader: "", companyFooter: "", diff --git a/src/store/StateStore/Models.js b/src/store/StateStore/Models.js index f042813..48b3e7f 100644 --- a/src/store/StateStore/Models.js +++ b/src/store/StateStore/Models.js @@ -23,6 +23,7 @@ export const ModelSales = { discountSelectionStatus: types.optional(types.boolean, true), fetching: types.optional(types.boolean, false), confirmOrder: types.optional(types.boolean, false), + confirmation: types.optional(types.boolean, false), addReceiptLineStatus: types.optional(types.boolean, false), }; export const ModelListing = { @@ -54,6 +55,7 @@ export const ModelPayment = { modalVisible: types.optional(types.boolean, false), customerName: types.optional(types.string, ""), customerEmail: types.optional(types.string, ""), + walletCardNumber: types.optional(types.string, ""), customerPhoneNumber: types.optional(types.string, ""), customerNotes: types.optional(types.string, ""), arrayObjects: types.optional(types.string, "[]"), @@ -64,11 +66,18 @@ export const ModelSettings = { connectionStatus: types.optional(types.string, "Not Connected"), currentAddress: types.optional(types.string, ""), companyName: types.optional(types.string, ""), + changeNoReceipts: types.optional(types.string, ""), tax: types.optional(types.string, "0"), companyHeader: types.optional(types.string, ""), companyFooter: types.optional(types.string, ""), companyCurrency: types.optional(types.string, ""), checkBoxBluetoothValue: types.optional(types.boolean, false), + smallSizeIcon: types.optional(types.boolean, false), + mediumSizeIcon: types.optional(types.boolean, false), + largeSizeIcon: types.optional(types.boolean, true), + multipleMop: types.optional(types.boolean, false), + allowRoundOff: types.optional(types.boolean, false), + hideMenuBar: types.optional(types.boolean, false), checkBoxValue: types.optional(types.string, ""), attendants: types.optional(types.string, "[]"), // attendantsInfo: {}, diff --git a/src/store/StateStore/StateStore.js b/src/store/StateStore/StateStore.js index b135c7a..d02a061 100644 --- a/src/store/StateStore/StateStore.js +++ b/src/store/StateStore/StateStore.js @@ -32,7 +32,16 @@ const StateStore = types currentTable: types.optional(types.number, -1), isViewingOrder: types.optional(types.boolean, false), isLoadingOrder: types.optional(types.boolean, false), - + currentConfirmation: types.optional(types.string, ""), + index_value: types.optional(types.number, 0), + discount_string: types.optional(types.string, "{}"), + receipt_summary: types.optional(types.string, "{}"), + scanned_nfc: types.optional(types.string, "{}"), + payment_types: types.optional(types.string, "[]"), + payment_amount: types.optional(types.string, "0"), + balance: types.optional(types.string, "0"), + customers_pin: types.optional(types.boolean, false), + customers_pin_value: types.optional(types.string, ""), // Settings queueHost: types.optional(types.string, ""), hasTailOrder: types.optional(types.boolean, false), @@ -40,6 +49,7 @@ const StateStore = types useDescription: types.optional(types.boolean, false), isHttps: types.optional(types.boolean, false), isCurrencyDisabled: types.optional(types.boolean, false), + enableOverallTax: types.optional(types.boolean, false), deviceId: types.optional(types.string, ""), isStackItem: types.optional(types.boolean, false), @@ -76,6 +86,21 @@ const StateStore = types }); }); }, + updateScannedNfc(key, value) { + let scanned_nfc = JSON.parse(self.scanned_nfc); + + scanned_nfc[key] = value; + self.scanned_nfc = JSON.stringify(scanned_nfc); + }, + is_customers_pin() { + self.customers_pin = true; + }, + is_not_customers_pin() { + self.customers_pin = true; + }, + set_receipt_summary(data) { + self.receipt_summary = data; + }, setDefaultValues(containerName, objectValue) { let containerNameValue = ""; if (containerName === "Sales") { @@ -117,9 +142,57 @@ const StateStore = types } }); }, + resetPaymentTypes() { + self.payment_types = "[]"; + }, + + resetScannedNfc() { + self.scanned_nfc = "{}"; + }, + addPaymentTypes(obj) { + let payment_types = JSON.parse(self.payment_types); + payment_types.push(obj); + self.payment_types = JSON.stringify(payment_types); + }, + updatePaymentType(obj) { + if (obj) { + let objectLength = JSON.parse(self.payment_types); + let exists = false; + for (let i = 0; i < objectLength.length; i += 1) { + if (obj.type === objectLength[i].type) { + objectLength[i].amount = obj.amount; + exists = true; + } + } + if (!exists) { + self.addPaymentTypes({ + type: obj.type, + amount: obj.amount, + }); + } else { + self.payment_types = JSON.stringify(objectLength); + } + } + }, + removePaymentType() { + let objectLength = JSON.parse(self.payment_types); + let filtered_items = objectLength.filter( + payment_type => payment_type.type !== self.payment_state[0].selected, + ); + self.payment_types = JSON.stringify(filtered_items); + }, setPaymentValue(value) { self.payment_value = value; }, + set_customers_pin(value) { + self.customers_pin_value = value; + }, + setMopAmount(value) { + self.payment_amount = value; + }, + setBalance(value) { + self.balance = value; + }, setAmountDue(value) { self.amount_due = value; }, @@ -129,6 +202,12 @@ const StateStore = types setLoadingOrder(isLoadingOrder) { self.isLoadingOrder = isLoadingOrder; }, + changeConfirmation(currentConfirmation) { + self.currentConfirmation = currentConfirmation; + }, + changeIndex(index) { + self.index_value = index; + }, setOrders(orders) { self.orders = orders; }, @@ -141,6 +220,9 @@ const StateStore = types setQueueHost(host) { self.queueHost = host; }, + changeDiscountString(discount) { + self.discount_string = discount; + }, toggleTailOrder() { self.hasTailOrder = !self.hasTailOrder; }, @@ -192,6 +274,9 @@ const StateStore = types toggleCurrencyDisabled() { self.isCurrencyDisabled = !self.isCurrencyDisabled; }, + toggleEnableOverallTax() { + self.enableOverallTax = !self.enableOverallTax; + }, setDeviceId(deviceId) { self.deviceId = deviceId; }, @@ -201,6 +286,9 @@ const StateStore = types changeCompanyCheckBox(isCurrencyDisabled) { self.isCurrencyDisabled = isCurrencyDisabled; }, + changeOverallTax(overallTax) { + self.enableOverallTax = overallTax; + }, })); const Store = StateStore.create({}); diff --git a/src/stories/components/AttendantsFormComponent.js b/src/stories/components/AttendantsFormComponent.js index 2ad4cfd..5249c62 100644 --- a/src/stories/components/AttendantsFormComponent.js +++ b/src/stories/components/AttendantsFormComponent.js @@ -20,6 +20,7 @@ class AddAttendantComponent extends React.Component { status: "Save Attendant", role: "Owner", canLogin: false, + canApprove: false, commission: "", }; } @@ -59,6 +60,7 @@ class AddAttendantComponent extends React.Component { securityPinStatus: true, securityConfirmPinStatus: true, canLogin: attendantInfo.canLogin, + canApprove: attendantInfo.canApprove, commission: attendantInfo.commission.toString(), }); } else { @@ -72,6 +74,7 @@ class AddAttendantComponent extends React.Component { status: "Save Attendant", role: "Owner", canLogin: false, + canApprove: false, commission: "", }); } @@ -111,6 +114,10 @@ class AddAttendantComponent extends React.Component { const { canLogin } = this.state; this.setState({ canLogin: !canLogin }); }; + toggleCanApprove = () => { + const { canApprove } = this.state; + this.setState({ canApprove: !canApprove }); + }; onPress = () => { const { status } = this.state; @@ -201,7 +208,7 @@ class AddAttendantComponent extends React.Component { strings.setLanguage(currentLanguage().companyLanguage); const { rolesData } = this.props; - const { attendantName, role, canLogin } = this.state; + const { attendantName, role, canLogin, canApprove } = this.state; const Roles = rolesData.map(this.renderRoles); @@ -234,8 +241,17 @@ class AddAttendantComponent extends React.Component { /> {strings.CanLogin} - {canLogin ? this.renderPin() : null} - {canLogin ? this.renderConfirmPin() : null} + + + Can Approve + + {canLogin || canApprove ? this.renderPin() : null} + {canLogin || canApprove ? this.renderConfirmPin() : null} diff --git a/src/stories/components/CategoriesComponent.js b/src/stories/components/CategoriesComponent.js index dfdfc45..34a3793 100644 --- a/src/stories/components/CategoriesComponent.js +++ b/src/stories/components/CategoriesComponent.js @@ -1,6 +1,7 @@ import * as React from "react"; import { FlatList, View, Dimensions } from "react-native"; import { Text, Button } from "native-base"; + import { currentLanguage } from "../../translations/CurrentLanguage"; import translation from "../.././translations/translation"; @@ -44,7 +45,7 @@ export default class CategoriesComponent extends React.PureComponent { style={{ height: this.props.bluetoothStatus ? Dimensions.get("window").height * 0.75 - : Dimensions.get("window").height * 0.85, + : Dimensions.get("window").height * 1.05, }} > + + + + ); + } +} + +const styles = StyleSheet.create({ + view: { + flex: 1, + alignItems: "center", + flexDirection: "column", + justifyContent: "center", + backgroundColor: "#00000090", + }, + innerView: { + width: 240, + backgroundColor: "white", + }, + headerView: { + padding: 10, + borderBottomWidth: 0.5, + borderBottomColor: "#eee", + flexDirection: "row", + justifyContent: "space-between", + }, + headerText: { + color: "gray", + fontWeight: "bold", + }, + close: { + alignSelf: "flex-end", + }, + item: { + backgroundColor: "#eee", + }, + input: { + paddingRight: 15, + fontWeight: "bold", + }, + setButton: { + borderRadius: 0, + }, +}); diff --git a/src/stories/components/EntriesComponent.js b/src/stories/components/EntriesComponent.js index 204228f..dc553e5 100644 --- a/src/stories/components/EntriesComponent.js +++ b/src/stories/components/EntriesComponent.js @@ -7,6 +7,8 @@ export default class EntriesComponent extends React.PureComponent { _renderItem = ({ item, index }) => { return ( ); }; + render() { + let numCol = + this.props.listStatus === "Sales" + ? this.props.company.smallSizeIcon + ? 4 + : 3 + : 2; return ( index} renderItem={this._renderItem} diff --git a/src/stories/components/EntryComponent.js b/src/stories/components/EntryComponent.js index 72940a3..5700f55 100644 --- a/src/stories/components/EntryComponent.js +++ b/src/stories/components/EntryComponent.js @@ -11,13 +11,34 @@ export default class EntryComponent extends React.PureComponent { let mc = new MoneyCurrency( this.props.currency ? this.props.currency : "PHP", ); + const { smallSizeIcon, mediumSizeIcon } = this.props.company; + const { listStatus } = this.props; + const height = Dimensions.get("window").height; return ( - + {this.props.value.colorAndShape ? ( ) : ( )} @@ -79,16 +131,31 @@ export default class EntryComponent extends React.PureComponent { style={{ alignItems: "center", justifyContent: "center", - width: Dimensions.get("window").width * 0.12, - height: Dimensions.get("window").height * 0.08, + width: + listStatus === "Sales" + ? smallSizeIcon + ? width * 0.06 + : mediumSizeIcon + ? width * 0.09 + : width * 0.12 + : width * 0.12, + height: height * 0.08, }} > {this.props.useDescription diff --git a/src/stories/components/GrandTotalComponent.js b/src/stories/components/GrandTotalComponent.js index 366cd2c..8bb6171 100644 --- a/src/stories/components/GrandTotalComponent.js +++ b/src/stories/components/GrandTotalComponent.js @@ -21,14 +21,13 @@ const get_tailorder_button = (props, currentTable) => { ); } - return ( ); }; @@ -88,6 +87,9 @@ const styles = StyleSheet.create({ paddingRight: 10, alignSelf: "center", }, + confirmButton: { + fontSize: Dimensions.get("window").width * 0.01, + }, }); export default GrandTotalComponent; diff --git a/src/stories/components/ModalKeypadComponent.js b/src/stories/components/ModalKeypadComponent.js index 0155c10..96bedbe 100644 --- a/src/stories/components/ModalKeypadComponent.js +++ b/src/stories/components/ModalKeypadComponent.js @@ -44,7 +44,11 @@ export default class ModalKeypadComponent extends React.PureComponent { - + {"noPeriod" in this.props ? ( + null} /> + ) : ( + + )} diff --git a/src/stories/components/MoreSettingsComponent.js b/src/stories/components/MoreSettingsComponent.js new file mode 100644 index 0000000..a1af2cf --- /dev/null +++ b/src/stories/components/MoreSettingsComponent.js @@ -0,0 +1,155 @@ +import * as React from "react"; +import { Dimensions, View, StyleSheet } from "react-native"; +import { Text, Card, CardItem } from "native-base"; +import { Col, Grid } from "react-native-easy-grid"; +import { currentLanguage } from "../../translations/CurrentLanguage"; + +import EditInput from "./EditInputComponent"; +import EditCheckBox from "./EditCheckBoxComponent"; +import translation from "../.././translations/translation"; +import LocalizedStrings from "react-native-localization"; +let strings = new LocalizedStrings(translation); +class MoreSettingsComponent extends React.PureComponent { + render() { + const { + toggleItemSize, + toggleMultipleMop, + toggleAllowRoundOff, + toggleHideMenuBar, + } = this.props; + const { + smallSizeIcon, + mediumSizeIcon, + largeSizeIcon, + multipleMop, + allowRoundOff, + hideMenuBar, + } = this.props.values; + strings.setLanguage(currentLanguage().companyLanguage); + + return ( + + + + + + More Settings + + + + + + + + + toggleMultipleMop("Medium")} + /> + + + + + + + + + + Item Icon Size + + + toggleItemSize("Small")} + /> + + + toggleItemSize("Medium")} + /> + + + toggleItemSize("Large")} + /> + + + + ); + } +} +const styles = StyleSheet.create({ + card: { + width: "100%", + alignSelf: "center", + }, + cardItem: { + marginBottom: 15, + backgroundColor: "#4b4c9d", + }, + col: { + alignSelf: "center", + }, + titleText: { + color: "white", + fontSize: Dimensions.get("window").width * 0.02, + }, + icon: { + color: "white", + marginLeft: 10, + }, + viewRight: { + flexDirection: "row", + alignSelf: "flex-end", + }, + cardItemHelp: { + borderColor: "gray", + borderBottomWidth: 0.5, + }, + cardItemText: { + marginLeft: 10, + fontWeight: "bold", + color: "gray", + }, + cardItemView: { + width: "50%", + marginLeft: 3, + }, + cardItemViewTextAreaTax: { + width: "50%", + marginLeft: 3, + }, + cardItemViewTextArea: { + width: "60%", + }, + pickerView: { + borderWidth: 1, + borderColor: "#cfcfcf", + }, + picker: { + width: "100%", + }, + text: { + fontWeight: "bold", + }, +}); + +export default MoreSettingsComponent; diff --git a/src/stories/components/NumberKeysComponent.js b/src/stories/components/NumberKeysComponent.js index 52c1c09..3b5ed47 100644 --- a/src/stories/components/NumberKeysComponent.js +++ b/src/stories/components/NumberKeysComponent.js @@ -1,6 +1,6 @@ import * as React from "react"; import { Dimensions, Text, FlatList } from "react-native"; -import { Form, Item, Button, Input } from "native-base"; +import { Form, Item, Button, Input, View } from "native-base"; import Icon from "react-native-vector-icons/FontAwesome"; var MoneyCurrency = require("money-currencies"); import { currentLanguage } from "../../translations/CurrentLanguage"; @@ -23,7 +23,6 @@ export default class NumberKeysComponent extends React.PureComponent { }; render() { strings.setLanguage(currentLanguage().companyLanguage); - let mc = new MoneyCurrency( this.props.currency ? this.props.currency : "PHP", ); @@ -42,47 +41,77 @@ export default class NumberKeysComponent extends React.PureComponent { underlineColorAndroid="transparent" /> + - + + {this.props.mop === "Wallet" ? ( + + {!("customer" in this.props.scanned_nfc) || + !("attendant" in this.props.scanned_nfc) ? ( + + Waiting for nfc card... + + ) : ( + + Please use this keypad to enter customers pin + + )} + + ) : ( + + )} ); } diff --git a/src/stories/components/OrderItemComponent.js b/src/stories/components/OrderItemComponent.js index f1bb001..24b8a97 100644 --- a/src/stories/components/OrderItemComponent.js +++ b/src/stories/components/OrderItemComponent.js @@ -13,44 +13,90 @@ class OrderItemComponent extends React.PureComponent { render() { strings.setLanguage(currentLanguage().companyLanguage); - const { id, tableNo, isTakeAway } = this.props; - + const { id, tableNo, isTakeAway, company, type } = this.props; + const size = company.smallSizeIcon + ? styles.smallSizeIcon + : company.mediumSizeIcon + ? styles.mediumSizeIcon + : styles.largeSizeIcon; + const text = company.smallSizeIcon + ? styles.smalltext + : company.mediumSizeIcon + ? styles.mediumtext + : styles.largetext; + const orderText = company.smallSizeIcon + ? styles.smallOrderText + : company.mediumSizeIcon + ? styles.mediumOrderText + : styles.largeOrderText; return ( - - + + [{strings.ORDER}-{id}] - - {strings.TableNo} {tableNo} + + {type} {strings.TableNo} {tableNo} ); } } - const styles = StyleSheet.create({ view: { margin: 15, - width: 180, - height: 180, borderRadius: 180 / 2, justifyContent: "center", backgroundColor: "#afafaf", }, + largeSizeIcon: { + width: 160, + height: 160, + }, + mediumSizeIcon: { + width: 110, + height: 110, + }, + smallSizeIcon: { + width: 80, + height: 80, + }, takeAwayView: { backgroundColor: "#ffb020", }, - text: { + //SMALL + smalltext: { + fontSize: 13, + fontWeight: "bold", + textAlign: "center", + }, + smallOrderText: { + fontSize: 12, + fontWeight: "bold", + textAlign: "center", + }, + //MEDIUM + mediumtext: { + fontSize: 17, + fontWeight: "bold", + textAlign: "center", + }, + mediumOrderText: { + fontSize: 15, + fontWeight: "bold", + textAlign: "center", + }, + //LARGE + largetext: { fontSize: 21, fontWeight: "bold", textAlign: "center", }, - orderText: { + largeOrderText: { fontSize: 18, fontWeight: "bold", textAlign: "center", diff --git a/src/stories/components/PrinterComponent.js b/src/stories/components/PrinterComponent.js index 3e487ad..e9dd48a 100644 --- a/src/stories/components/PrinterComponent.js +++ b/src/stories/components/PrinterComponent.js @@ -30,7 +30,7 @@ const PrinterComponent = props => { const connectionStatus = ( - {props.connection ? strings.Online : props.connectionStatus} + {props.connection ? "Online" : props.connectionStatus} ); diff --git a/src/stories/components/QuantityModalComponent.js b/src/stories/components/QuantityModalComponent.js index 618d565..7b2d0f5 100644 --- a/src/stories/components/QuantityModalComponent.js +++ b/src/stories/components/QuantityModalComponent.js @@ -470,16 +470,7 @@ export default class QuantityModalComponent extends React.Component { this.props.onSubmit(this.state); }} > - - {strings.Set}{" "} - {this.state.status === "Qty" - ? "quantity" - : this.state.status === "Price" - ? "price" - : this.state.status === "Commission" - ? "commission" - : ""} - + Edit Transaction diff --git a/src/stories/components/SingleReportComponent.js b/src/stories/components/SingleReportComponent.js index 1365ff6..2752efd 100644 --- a/src/stories/components/SingleReportComponent.js +++ b/src/stories/components/SingleReportComponent.js @@ -10,6 +10,7 @@ let MoneyCurrency = require("money-currencies"); import translation from "../.././translations/translation"; import LocalizedStrings from "react-native-localization"; let strings = new LocalizedStrings(translation); + const SingleReportComponent = props => { strings.setLanguage(currentLanguage().companyLanguage); const categoryAmounts = JSON.parse(props.report.categories_total_amounts).map( @@ -303,6 +304,28 @@ const SingleReportComponent = props => { + + + Cancelled + + + + {props.isCurrencyDisabled + ? formatNumber(props.report.cancelled) + : new MoneyCurrency( + props.currency ? props.currency : "PHP", + ).moneyFormat(formatNumber(props.report.cancelled))} + + + + + + Voided + + + {props.report.voided} + + diff --git a/src/stories/components/SummaryModalComponent.js b/src/stories/components/SummaryModalComponent.js index eb2dd2a..8fc1e34 100644 --- a/src/stories/components/SummaryModalComponent.js +++ b/src/stories/components/SummaryModalComponent.js @@ -97,11 +97,20 @@ export default class SummaryModalComponent extends React.Component { - {mc.moneyFormat( - formatNumber( - parseFloat(this.props.details.get_tax_total), - ), - )} + {this.props.enableOverallTax + ? mc.moneyFormat( + formatNumber( + parseFloat(this.props.details.get_tax_total), + ), + ) + : mc.moneyFormat( + formatNumber( + parseFloat( + this.props.details + .get_tax_total_based_on_each_item, + ), + ), + )} diff --git a/src/stories/components/TabComponent.js b/src/stories/components/TabComponent.js index c77c6b0..31a1e70 100644 --- a/src/stories/components/TabComponent.js +++ b/src/stories/components/TabComponent.js @@ -6,6 +6,7 @@ const TabComponent = props => ( props.onClick(index)} diff --git a/src/stories/components/TotalLineComponent.js b/src/stories/components/TotalLineComponent.js index fc80443..f41e1e7 100644 --- a/src/stories/components/TotalLineComponent.js +++ b/src/stories/components/TotalLineComponent.js @@ -25,7 +25,9 @@ const TotalLineComponent = props => ( {strings.Tax}{" "} {parseFloat(props.receipt.taxesValue) > 0 - ? "(" + props.receipt.taxesValue.toString() + "%)" + ? props.enableOverallTax + ? "(" + props.receipt.taxesValue.toString() + "%)" + : "" : ""} diff --git a/src/stories/components/ViewOrderComponent.js b/src/stories/components/ViewOrderComponent.js index c552174..4861e97 100644 --- a/src/stories/components/ViewOrderComponent.js +++ b/src/stories/components/ViewOrderComponent.js @@ -20,21 +20,25 @@ import LocalizedStrings from "react-native-localization"; let strings = new LocalizedStrings(translation); class ViewOrderComponent extends React.PureComponent { renderOrderItem = ({ item, index }) => { - const { onTableClick, onTableLongPress } = this.props; - return ( - - ); - }; + const { onTableClick, onTableLongPress, company } = this.props; + if (!item.is_fulfilled) { + return ( + + ); + } + }; renderOrders() { - const { orders } = this.props; + const { orders, company } = this.props; if (orders.length === 0) { return ( @@ -50,7 +54,7 @@ class ViewOrderComponent extends React.PureComponent { return ( ); diff --git a/src/stories/screens/InputItem/frm.js b/src/stories/screens/InputItem/frm.js index d9a97d7..d97d869 100644 --- a/src/stories/screens/InputItem/frm.js +++ b/src/stories/screens/InputItem/frm.js @@ -27,6 +27,9 @@ export default class Form { const newPrice = isCurrencyDisabled ? price : price.slice(1); this._setState({ price: newPrice }); }; + onChangeTax = tax => { + this._setState({ tax: tax }); + }; setSoldByEach = () => { this._setState({ soldBy: "Each" }); }; diff --git a/src/stories/screens/InputItem/index.js b/src/stories/screens/InputItem/index.js index a4a5c77..14dabda 100644 --- a/src/stories/screens/InputItem/index.js +++ b/src/stories/screens/InputItem/index.js @@ -38,24 +38,24 @@ export default class InputItem extends React.Component { if (data) { const priceCurrency = formatNumber(data.price); - this.setState({ name: data.name, price: priceCurrency, sku: data.sku, + tax: data.tax.toString(), barcode: data.barcode, category: data.category, soldBy: data.soldBy, colorAndShape: JSON.parse(data.colorAndShape), }); } - if (dataDetails) { const dataDupBarcode = JSON.parse(dataDetails); this.setState({ name: dataDupBarcode.name, price: dataDupBarcode.price.toString(), sku: dataDupBarcode.sku, + tax: dataDupBarcode.tax, barcode: dataDupBarcode.barcode, category: dataDupBarcode.category, soldBy: dataDupBarcode.soldBy, @@ -70,6 +70,7 @@ export default class InputItem extends React.Component { status: "item", name: "", price: "0.00", + tax: "0", sku: "", barcode: "", category: "No Category", @@ -219,6 +220,21 @@ export default class InputItem extends React.Component { + + + + + this.frm.onChangeTax(value)} + /> + + + diff --git a/src/stories/screens/Payment/index.js b/src/stories/screens/Payment/index.js index 8f45254..d89c7df 100644 --- a/src/stories/screens/Payment/index.js +++ b/src/stories/screens/Payment/index.js @@ -12,6 +12,8 @@ import { Item, Input, Picker, + Textarea, + Text, } from "native-base"; import { View, Alert, StyleSheet } from "react-native"; import { formatNumber } from "accounting-js"; @@ -34,13 +36,22 @@ const PAYMENT_ITEMS = [ , , , + , ]; export default class Payment extends React.PureComponent { onValueChange = text => { this.props.onValueChange(text); }; - + payment_type = () => { + const { paymentTypes } = this.props; + let payment_types_values = ""; + for (let i = 0; i < paymentTypes.length; i += 1) { + payment_types_values += + paymentTypes[i].type + " - " + paymentTypes[i].amount.toString() + "\n"; + } + return payment_types_values; + }; onPay = () => { Alert.alert( strings.ConfirmPayment, @@ -88,7 +99,10 @@ export default class Payment extends React.PureComponent { let mc = new MoneyCurrency( this.props.currency ? this.props.currency : "PHP", ); - const amountValue = parseFloat(this.props.paymentValue); + const amountValue = this.props.settings_state.multipleMop + ? parseFloat(this.props.payment_amount_multiple) + : parseFloat(this.props.paymentValue); + const amountDue = parseFloat(this.props.amountDue); let change = 0; @@ -112,12 +126,36 @@ export default class Payment extends React.PureComponent { + {this.props.settings_state.multipleMop ? ( + + + + + {PAYMENT_ITEMS} + + + + + + ) : null} @@ -152,18 +190,58 @@ export default class Payment extends React.PureComponent { /> - {this.renderCustomer()} + {/*{this.renderCustomer()}*/} - - - - {PAYMENT_ITEMS} - - + {this.props.settings_state.multipleMop ? ( + + +