Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
1156 lines (1092 loc) · 54.7 KB
/
Copy pathindex.html
File metadata and controls
1156 lines (1092 loc) · 54.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<title>Van Sale — User Guide</title>
<style>
:root {
--accent: #2563eb;
--accent-deep: #1b3fa8;
--accent-soft: #e8eeff;
--ink: #131a24;
--ink-2: #3d4a5a;
--muted: #5a6673;
--line: #dde3ea;
--line-soft: #eaeef4;
--paper: #f6f8fb;
--surface: #ffffff;
--ok: #0f7a52;
--warn: #a85c00;
--warn-soft: #fdf3e3;
--crit: #b3261e;
--crit-soft: #fdeceb;
--sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
--measure: 68ch;
--r: 8px;
}
@media (prefers-color-scheme: dark) {
:root {
--accent: #6d9bff;
--accent-deep: #a8c2ff;
--accent-soft: #17233a;
--ink: #e6ebf2;
--ink-2: #bcc7d6;
--muted: #8e9cad;
--line: #263243;
--line-soft: #1d2734;
--paper: #0e141b;
--surface: #161e28;
--ok: #4cc38a;
--warn: #e0a252;
--warn-soft: #2a2013;
--crit: #f2837b;
--crit-soft: #2c1615;
}
}
:root[data-theme="dark"] {
--accent: #6d9bff;
--accent-deep: #a8c2ff;
--accent-soft: #17233a;
--ink: #e6ebf2;
--ink-2: #bcc7d6;
--muted: #8e9cad;
--line: #263243;
--line-soft: #1d2734;
--paper: #0e141b;
--surface: #161e28;
--ok: #4cc38a;
--warn: #e0a252;
--warn-soft: #2a2013;
--crit: #f2837b;
--crit-soft: #2c1615;
}
:root[data-theme="light"] {
--accent: #2563eb;
--accent-deep: #1b3fa8;
--accent-soft: #e8eeff;
--ink: #131a24;
--ink-2: #3d4a5a;
--muted: #5a6673;
--line: #dde3ea;
--line-soft: #eaeef4;
--paper: #f6f8fb;
--surface: #ffffff;
--ok: #0f7a52;
--warn: #a85c00;
--warn-soft: #fdf3e3;
--crit: #b3261e;
--crit-soft: #fdeceb;
}
body {
margin: 0;
background: var(--paper);
color: var(--ink);
font-family: var(--sans);
font-size: 16px;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
.wrap {
max-width: 1180px;
margin: 0 auto;
padding: 0 1.25rem 6rem;
display: grid;
grid-template-columns: 232px minmax(0, 1fr);
gap: 3rem;
align-items: start;
}
@media (max-width: 900px) {
.wrap { grid-template-columns: minmax(0, 1fr); gap: 0; }
.toc { position: static; border-inline-end: 0; border-block-end: 1px solid var(--line); margin-bottom: 2rem; }
}
/* ---------- masthead ---------- */
.masthead {
border-bottom: 1px solid var(--line);
background: var(--surface);
margin-bottom: 2.5rem;
}
.masthead-in {
max-width: 1180px;
margin: 0 auto;
padding: 2.75rem 1.25rem 2rem;
}
.eyebrow {
font-size: 0.7rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--muted);
font-weight: 650;
margin: 0 0 0.6rem;
}
h1 {
font-size: clamp(2rem, 5vw, 3rem);
line-height: 1.05;
letter-spacing: -0.03em;
font-weight: 700;
margin: 0 0 0.75rem;
text-wrap: balance;
}
.standfirst {
font-size: 1.075rem;
color: var(--ink-2);
max-width: var(--measure);
margin: 0 0 1.5rem;
}
.meta {
display: flex;
flex-wrap: wrap;
gap: 0.4rem 0.5rem;
font-family: var(--mono);
font-size: 0.75rem;
}
.meta span {
border: 1px solid var(--line);
border-radius: var(--r);
padding: 0.2rem 0.5rem;
color: var(--muted);
background: var(--paper);
}
/* ---------- toc ---------- */
.toc {
position: sticky;
top: 1.5rem;
font-size: 0.875rem;
padding-inline-end: 1rem;
border-inline-end: 1px solid var(--line-soft);
}
.toc h2 {
font-size: 0.68rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--muted);
margin: 0 0 0.6rem;
}
.toc ol { list-style: none; margin: 0 0 1.5rem; padding: 0; display: flex; flex-direction: column; gap: 0.15rem; }
.toc a {
color: var(--ink-2);
text-decoration: none;
display: block;
padding: 0.2rem 0;
border-radius: 4px;
}
.toc a:hover, .toc a:focus-visible { color: var(--accent); text-decoration: underline; }
.toc .num { font-family: var(--mono); font-size: 0.75rem; color: var(--muted); margin-inline-end: 0.4rem; }
/* ---------- content ---------- */
main { min-width: 0; }
section { margin-bottom: 3.5rem; scroll-margin-top: 1.5rem; }
h2.part {
font-size: 0.7rem;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--accent);
font-weight: 700;
margin: 0 0 0.5rem;
padding-top: 1.5rem;
border-top: 2px solid var(--accent);
}
h3 {
font-size: clamp(1.3rem, 2.6vw, 1.65rem);
letter-spacing: -0.02em;
line-height: 1.2;
margin: 0 0 0.75rem;
text-wrap: balance;
}
h4 {
font-size: 1.02rem;
margin: 2rem 0 0.5rem;
letter-spacing: -0.01em;
}
h4 .step {
font-family: var(--mono);
font-size: 0.8rem;
color: var(--accent);
margin-inline-end: 0.45rem;
}
p, ul, ol { max-width: var(--measure); }
p { margin: 0 0 0.9rem; }
ul, ol { margin: 0 0 0.9rem; padding-inline-start: 1.25rem; }
li { margin-bottom: 0.35rem; }
strong { font-weight: 650; }
a { color: var(--accent); }
code, .f {
font-family: var(--mono);
font-size: 0.855em;
background: var(--accent-soft);
color: var(--accent-deep);
padding: 0.1em 0.35em;
border-radius: 4px;
word-break: break-word;
}
/* ---------- tables ---------- */
.tw { overflow-x: auto; margin: 0 0 1.25rem; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }
table { border-collapse: collapse; width: 100%; font-size: 0.9rem; }
th, td { text-align: start; padding: 0.65rem 0.8rem; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
th {
font-size: 0.68rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
background: var(--paper);
white-space: nowrap;
font-weight: 650;
}
tbody tr:last-child td { border-bottom: 0; }
td.f-cell { font-family: var(--mono); font-size: 0.8rem; white-space: nowrap; color: var(--accent-deep); }
.num-col { font-variant-numeric: tabular-nums; }
.chip {
display: inline-block;
font-family: var(--mono);
font-size: 0.7rem;
padding: 0.1rem 0.4rem;
border-radius: 4px;
border: 1px solid var(--line);
color: var(--muted);
white-space: nowrap;
}
.chip.on { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, transparent); }
.chip.off { color: var(--muted); }
/* ---------- callouts ---------- */
.note {
border: 1px solid var(--line);
border-inline-start: 3px solid var(--accent);
background: var(--surface);
border-radius: var(--r);
padding: 0.9rem 1.1rem;
margin: 0 0 1.25rem;
max-width: var(--measure);
}
.note.warn { border-inline-start-color: var(--warn); background: var(--warn-soft); }
.note.crit { border-inline-start-color: var(--crit); background: var(--crit-soft); }
.note p:last-child { margin-bottom: 0; }
.note .tag {
display: block;
font-size: 0.68rem;
letter-spacing: 0.12em;
text-transform: uppercase;
font-weight: 700;
margin-bottom: 0.35rem;
color: var(--accent);
}
.note.warn .tag { color: var(--warn); }
.note.crit .tag { color: var(--crit); }
/* ---------- figures ---------- */
figure { margin: 1.5rem 0; }
figcaption {
font-size: 0.8rem;
color: var(--muted);
margin-top: 0.5rem;
max-width: var(--measure);
}
figcaption b { color: var(--ink-2); font-family: var(--mono); font-size: 0.75rem; font-weight: 600; }
.shot {
border: 1px dashed color-mix(in srgb, var(--accent) 45%, var(--line));
border-radius: var(--r);
background: var(--surface);
padding: 1rem;
display: flex;
gap: 1rem;
align-items: flex-start;
}
.shot .slot {
flex: none;
width: 4.5rem;
height: 4.5rem;
border-radius: var(--r);
background: var(--accent-soft);
color: var(--accent-deep);
display: grid;
place-items: center;
font-family: var(--mono);
font-size: 0.7rem;
font-weight: 700;
text-align: center;
line-height: 1.3;
}
.shot .what { min-width: 0; font-size: 0.875rem; }
.shot .what p { margin: 0 0 0.3rem; }
.shot .path { font-family: var(--mono); font-size: 0.78rem; color: var(--muted); }
.frame {
border: 1px solid var(--line);
border-radius: 14px;
background: var(--surface);
padding: 0.5rem;
max-width: 320px;
box-shadow: 0 1px 3px rgb(0 0 0 / 6%);
}
.frame.wide { max-width: 100%; }
.frame svg { display: block; width: 100%; height: auto; border-radius: 9px; }
/* ---------- chain ---------- */
.chain { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin: 0 0 1.25rem; }
.chain .lnk {
border: 1px solid var(--line);
background: var(--surface);
border-radius: var(--r);
padding: 0.5rem 0.7rem;
font-size: 0.85rem;
}
.chain .lnk b { display: block; font-family: var(--mono); font-size: 0.72rem; color: var(--accent-deep); font-weight: 600; }
.chain .arrow { color: var(--muted); font-size: 1.1rem; }
.kv { display: grid; grid-template-columns: auto 1fr; gap: 0.3rem 0.9rem; font-size: 0.9rem; max-width: var(--measure); margin: 0 0 1.25rem; }
.kv dt { font-family: var(--mono); font-size: 0.8rem; color: var(--accent-deep); }
.kv dd { margin: 0; }
hr.rule { border: 0; border-top: 1px solid var(--line); margin: 2.5rem 0; }
@media (prefers-reduced-motion: reduce) {
* { animation: none !important; transition: none !important; }
}
/* Screenshots. Phone shots sit in a device frame at their natural width; desk
shots go full measure. `multi` pairs two related states side by side. */
.figshot { margin: 1.5rem 0; }
.figshot-imgs { display: flex; gap: 1rem; flex-wrap: wrap; align-items: flex-start; }
.figshot img {
display: block;
max-width: 100%;
height: auto;
border: 1px solid var(--line);
border-radius: 12px;
background: var(--surface);
}
.figshot.phone img { width: 268px; }
.figshot.desk img { width: 100%; }
.figshot.desk.multi img { width: calc(50% - 0.5rem); min-width: 320px; }
@media (max-width: 640px) {
.figshot.phone img { width: 100%; max-width: 320px; }
.figshot.desk.multi img { width: 100%; }
}
</style>
<header class="masthead">
<div class="masthead-in">
<p class="eyebrow">Enfono Technologies · Field operations</p>
<h1>Van Sale — User Guide</h1>
<p class="standfirst">
Everything the driver does on the phone, and everything the office sets up in
ERPNext to control it. Written against app v1.0.30.
</p>
<div class="meta">
<span>app v1.0.30</span>
<span>ERPNext v15</span>
<span>Part A · driver</span>
<span>Part B · office</span>
</div>
</div>
</header>
<div class="wrap">
<nav class="toc" aria-label="Contents">
<h2>Part A · Driver app</h2>
<ol>
<li><a href="#a1"><span class="num">A1</span>First run</a></li>
<li><a href="#a2"><span class="num">A2</span>Sign in & PIN</a></li>
<li><a href="#a3"><span class="num">A3</span>Home</a></li>
<li><a href="#a4"><span class="num">A4</span>Making a sale</a></li>
<li><a href="#a5"><span class="num">A5</span>Printing</a></li>
<li><a href="#a6"><span class="num">A6</span>Collecting payment</a></li>
<li><a href="#a7"><span class="num">A7</span>Returns</a></li>
<li><a href="#a8"><span class="num">A8</span>Route or Stock</a></li>
<li><a href="#a9"><span class="num">A9</span>Working offline</a></li>
<li><a href="#a10"><span class="num">A10</span>More menu</a></li>
</ol>
<h2>Part B · ERPNext</h2>
<ol>
<li><a href="#b1"><span class="num">B1</span>Where things live</a></li>
<li><a href="#b2"><span class="num">B2</span>Vansale Settings</a></li>
<li><a href="#b3"><span class="num">B3</span>Vansale Configuration</a></li>
<li><a href="#b4"><span class="num">B4</span>Assigned users</a></li>
<li><a href="#b5"><span class="num">B5</span>Warehouse & cost centre</a></li>
<li><a href="#b6"><span class="num">B6</span>Pricing</a></li>
<li><a href="#b7"><span class="num">B7</span>Tax</a></li>
<li><a href="#b8"><span class="num">B8</span>Document numbering</a></li>
<li><a href="#b9"><span class="num">B9</span>Precedence</a></li>
<li><a href="#b10"><span class="num">B10</span>Reports</a></li>
<li><a href="#b11"><span class="num">B11</span>Admin tasks</a></li>
<li><a href="#b12"><span class="num">B12</span>Troubleshooting</a></li>
<li><a href="#b13"><span class="num">B13</span>New van checklist</a></li>
<li><a href="#shots"><span class="num">§</span>Screenshot list</a></li>
</ol>
</nav>
<main>
<!-- =============================== PART A =============================== -->
<section id="partA">
<h2 class="part">Part A — The driver app</h2>
<h3>What the app is for</h3>
<p>
One Android app for selling off a van: invoice a customer at their shop, take
the cash, print the receipt, handle returns, and keep going when there is no
signal. Everything the driver books lands in ERPNext as a real Sales Invoice,
Payment Entry or Stock Entry — there is no separate ledger to reconcile.
</p>
<p>
Two things about the app are worth knowing before you start. It is
<strong>offline-first</strong>: work booked without signal is queued and sent
later, so a dead zone never stops a sale. And it is
<strong>scoped to one van</strong>: the driver only sees their own customers,
their own van's stock and their own documents.
</p>
</section>
<section id="a1">
<h4><span class="step">A1</span>First run — the server address</h4>
<p>
The first screen asks for your company's server address. This is entered
<strong>once per install</strong> and kept until the app is uninstalled.
</p>
<figure>
<div class="frame">
<svg viewBox="0 0 300 520" role="img" aria-label="Setup screen wireframe: logo, title, server address field, Connect button">
<rect width="300" height="520" fill="var(--paper)"/>
<rect x="122" y="120" width="56" height="56" rx="13" fill="var(--accent)"/>
<text x="150" y="154" font-family="var(--sans)" font-size="11" font-weight="700" fill="#fff" text-anchor="middle">LOGO</text>
<text x="150" y="205" font-family="var(--sans)" font-size="16" font-weight="700" fill="var(--ink)" text-anchor="middle">Fateh Van Sales</text>
<text x="150" y="228" font-family="var(--sans)" font-size="10" fill="var(--muted)" text-anchor="middle">Connect to your company server</text>
<text x="30" y="272" font-family="var(--sans)" font-size="9" font-weight="700" fill="var(--muted)">SERVER ADDRESS</text>
<rect x="28" y="282" width="244" height="40" rx="8" fill="var(--surface)" stroke="var(--line)"/>
<text x="42" y="307" font-family="var(--mono)" font-size="10" fill="var(--ink-2)">van.company.com</text>
<text x="30" y="342" font-family="var(--sans)" font-size="9" fill="var(--muted)">Ask your office for this address.</text>
<rect x="28" y="360" width="244" height="42" rx="8" fill="var(--accent)"/>
<text x="150" y="386" font-family="var(--sans)" font-size="13" font-weight="700" fill="#fff" text-anchor="middle">Connect</text>
<text x="150" y="424" font-family="var(--mono)" font-size="9" fill="var(--muted)" text-anchor="middle">v1.0.30</text>
</svg>
</div>
<figcaption><b>Figure A1</b> — the setup screen. No app chrome yet: there is nothing to navigate to until a server is set.</figcaption>
</figure>
<ul>
<li>Type the host only — <code>van.company.com</code>. <code>https://</code> is added for you.</li>
<li>If you paste a full browser URL, everything after the host is ignored.</li>
<li>The address is <strong>tested before it is saved</strong>, so a typo is caught here rather than failing silently on every screen afterwards.</li>
</ul>
<div class="note">
<span class="tag">If it will not connect</span>
<p>
<em>"Could not reach that address"</em> means the spelling or the signal —
check both. <em>"Not a Van Sale server"</em> means you reached a real server
that does not run this app; you have the wrong address, not a broken phone.
</p>
</div>
<div class="shot"><div class="slot">NATIVE<br>ONLY</div><div class="what"><p>The server-address screen only exists in the APK — the web build is always served by its own site, so there is nothing to configure. The wireframe above is drawn from the real screen.</p></div></div>
</section>
<section id="a2">
<h4><span class="step">A2</span>Signing in, and the PIN</h4>
<p>
Sign in once with the ERPNext email and password the office gave you. After
that the app stays signed in <strong>until it is uninstalled</strong> — the
password is not asked for again.
</p>
<p>
If PIN unlock is switched on for you, you choose a 4-digit PIN on first sign-in
and re-enter it after 2 hours idle. That is a quick unlock, not a new login.
Change it any time from <strong>More → Change PIN</strong>.
</p>
<div class="note">
<span class="tag">Forgot the PIN</span>
<p>
Nobody can read it back to you — call the office and ask them to reset it
(Part B11). Your queued offline work survives a PIN reset.
</p>
</div>
<figure class="figshot phone multi"><div class="figshot-imgs"><img src="img/shot-02.png" alt="Login screen" loading="lazy"><img src="img/shot-18.png" alt="PIN unlock keypad" loading="lazy"></div><figcaption><b>Figure A2</b> — signing in, and the PIN keypad on a later launch. Password is asked once per install; the PIN is a quick unlock.</figcaption></figure>
</section>
<section id="a3">
<h4><span class="step">A3</span>Home</h4>
<p>
Today's sales and collections at the top, four shortcuts under it, then recent
activity. The chip under the greeting shows which van and warehouse you are
working as — if that is wrong, stop and call the office; everything you book
will be attributed there.
</p>
<p>The bottom bar is fixed at five: <strong>Home · Sales · Customers · Route or Stock · More</strong>.</p>
<figure class="figshot phone"><div class="figshot-imgs"><img src="img/shot-03.png" alt="Home screen with the day's figures" loading="lazy"></div><figcaption><b>Figure A3</b> — Home. The chip under the greeting is the van and warehouse you are working as. This van has route planning off, so the fourth tab reads <b>Stock</b>.</figcaption></figure>
</section>
<section id="a4">
<h4><span class="step">A4</span>Making a sale</h4>
<p><strong>Home → New invoice</strong>, or <strong>Sales → +</strong>.</p>
<div class="tw">
<table>
<thead>
<tr><th>On the form</th><th>What to know</th></tr>
</thead>
<tbody>
<tr>
<td><strong>Customer</strong></td>
<td>Tap to open a search sheet and type any part of the name or code. Only your own customers are listed. <strong>New</strong> creates one on the spot.</td>
</tr>
<tr>
<td><strong>Catalog</strong></td>
<td>Only items <em>with stock in your van</em> appear. An empty catalog means the van is empty, not that the app is broken — the message tells you which warehouse to ask about.</td>
</tr>
<tr>
<td><strong>Qty / Rate</strong></td>
<td>Rate is pre-filled from the price list for this customer. You may override it if the office allows.</td>
</tr>
<tr>
<td><strong>UOM</strong></td>
<td>If enabled, switch a line between that item's own units (Pcs / Box / Carton) and the rate re-fetches for the new unit. Only that item's units are offered — never the whole UOM list.</td>
</tr>
<tr>
<td><strong>Discount</strong></td>
<td>Per line, or one invoice-level amount. The invoice-level amount is split across lines proportionally and the split is previewed before you save.</td>
</tr>
<tr>
<td><strong>Cash / Credit</strong></td>
<td>Cash records the payment with the invoice. Credit leaves it outstanding for collection later.</td>
</tr>
</tbody>
</table>
</div>
<p>
<strong>Save as draft</strong> keeps it editable and posts nothing.
<strong>Submit</strong> is final: stock leaves the van and the ledger is
written. A submitted invoice cannot be edited — correct it with a return.
</p>
<div class="note">
<span class="tag">About the tax figure</span>
<p>
The tax shown while you type is a live preview using this customer's real
tax rate — a zero-rated customer shows no tax at all. If your price list is
tax-inclusive, the line rate you type already contains the tax and the
totals show <span class="chip">incl.</span> next to the tax line. The final
breakup is always recomputed by the server on save.
</p>
</div>
<figure class="figshot phone multi"><div class="figshot-imgs"><img src="img/shot-15.png" alt="Customer search sheet" loading="lazy"><img src="img/shot-04.png" alt="Invoice totals with the tax line" loading="lazy"></div><figcaption><b>Figure A4</b> — the customer search sheet, and the totals after two lines. The tax line names the template that was actually resolved for this customer (<code>VAT 15%</code>) — not a fixed 15%.</figcaption></figure>
</section>
<section id="a5">
<h4><span class="step">A5</span>Printing</h4>
<p>
Open a submitted invoice and tap <strong>Print</strong>. The app fetches the
document and hands it to Android's own print dialog, so you can pick any
printer you have paired, or save a PDF.
</p>
<div class="note">
<span class="tag">No browser login needed</span>
<p>
Printing does not open a browser and does not ask you to sign in to ERPNext.
If you are ever asked to log in to print, you are on an old build.
</p>
</div>
<figure class="figshot phone"><div class="figshot-imgs"><img src="img/shot-05.png" alt="Submitted invoice detail with the Print button" loading="lazy"></div><figcaption><b>Figure A5</b> — a submitted invoice. <b>Print</b> hands the document to Android's own print dialog; no browser, no ERPNext login.</figcaption></figure>
</section>
<section id="a6">
<h4><span class="step">A6</span>Collecting payment</h4>
<p>
<strong>Home → Collect payment</strong>. Pick the customer, enter the amount,
choose the mode of payment. The app shows what they owe and applies the money
against the oldest invoices first.
</p>
<p>
Taking money without applying it to an invoice leaves it
<em>unapplied</em> — the office sees that separately in the Collection Summary
report, because it is the usual reason a customer's statement disagrees with
what the driver says they collected.
</p>
<figure class="figshot phone"><div class="figshot-imgs"><img src="img/shot-06.png" alt="Payment collection form" loading="lazy"></div><figcaption><b>Figure A6</b> — collecting payment. Outstanding is shown before you enter an amount.</figcaption></figure>
</section>
<section id="a7">
<h4><span class="step">A7</span>Returns</h4>
<p>
Open the original invoice → <strong>Return</strong>. Tick the lines coming
back and set the quantity; you cannot return more than was sold.
</p>
<p>
<strong>A reason is required.</strong> Pick one of
<span class="chip">Damaged</span> <span class="chip">Expired</span>
<span class="chip">Wrong Item</span> <span class="chip">Customer Refused</span>
<span class="chip">Short Delivery</span> <span class="chip">Other</span>
and add detail in the note box. Submit stays disabled until a reason is
chosen.
</p>
<p>
This is not paperwork for its own sake: the office reports returns by reason,
which is how a bad batch or a mis-picked route gets spotted.
</p>
<figure class="figshot phone"><div class="figshot-imgs"><img src="img/shot-07.png" alt="Return screen with a reason chip selected" loading="lazy"></div><figcaption><b>Figure A7</b> — a return. Submit stays disabled until a reason is chosen; the note box is for detail.</figcaption></figure>
</section>
<section id="a8">
<h4><span class="step">A8</span>Route, or Stock</h4>
<p>
The fourth tab is one or the other, never both — the office decides per van.
</p>
<div class="tw">
<table>
<thead><tr><th>Tab</th><th>What it does</th></tr></thead>
<tbody>
<tr>
<td><strong>Route</strong></td>
<td>Today's planned stops in order. Start a visit, sell or collect, then complete it. If the van is set to capture location, the app asks for GPS permission and tags the end of the visit. Signature capture lives here.</td>
</tr>
<tr>
<td><strong>Stock</strong></td>
<td>What is currently on board, by item. Use it to check before promising a customer.</td>
</tr>
</tbody>
</table>
</div>
<p>With Route on, Van Stock is still reachable from <strong>More</strong>.</p>
<figure class="figshot phone multi"><div class="figshot-imgs"><img src="img/shot-08.png" alt="Van stock list" loading="lazy"><img src="img/shot-19.png" alt="Today's route stop list" loading="lazy"></div><figcaption><b>Figure A8</b> — the fourth tab is one or the other. Left: <b>Stock</b>, what is on board. Right: <b>Route</b>, today's stops, on a van with route planning enabled.</figcaption></figure>
</section>
<section id="a9">
<h4><span class="step">A9</span>Working offline</h4>
<p>
Keep selling. Invoices, payments, returns, customers and stock moves are
queued on the phone and sent when signal returns. The sync badge in the header
shows how many items are waiting.
</p>
<p>Some queued items cannot be sent, and the app is explicit about whose problem it is:</p>
<div class="tw">
<table>
<thead><tr><th>Chip</th><th>Meaning</th><th>What you do</th></tr></thead>
<tbody>
<tr>
<td><span class="chip">Retrying</span></td>
<td>No signal yet.</td>
<td>Nothing. It goes on its own.</td>
</tr>
<tr>
<td><span class="chip">Needs edit</span></td>
<td>Something in what you entered is wrong or missing.</td>
<td>Tap <strong>Edit</strong>, fix it, retry.</td>
</tr>
<tr>
<td><span class="chip">Needs admin</span></td>
<td>A data problem in ERPNext — typically an item with no valuation, or no stock in the van. Editing cannot fix it.</td>
<td>Call the office. Once they fix it, tap <strong>Retry</strong>.</td>
</tr>
<tr>
<td><span class="chip">Not permitted</span></td>
<td>Your user lacks a permission.</td>
<td>Call the office.</td>
</tr>
</tbody>
</table>
</div>
<div class="note warn">
<span class="tag">Never uninstall with items pending</span>
<p>
Queued work lives on the phone. Uninstalling, clearing app data, or
switching the app to another server discards anything not yet sent. Check
the sync badge is clear first.
</p>
</div>
<figure class="figshot phone"><div class="figshot-imgs"><img src="img/shot-09.png" alt="Sync queue screen" loading="lazy"></div><figcaption><b>Figure A9</b> — the sync queue. Chips say whose problem an entry is: <b>Needs edit</b> is yours, <b>Needs admin</b> is the office's.</figcaption></figure>
</section>
<section id="a10">
<h4><span class="step">A10</span>The More menu</h4>
<dl class="kv">
<dt>Shortcuts</dt><dd>New invoice, all invoices, payments, returns, customers, van stock, route, sync errors.</dd>
<dt>Change PIN</dt><dd>Current PIN, then the new one twice.</dd>
<dt>Server</dt><dd>Shows the connected server. <strong>Change server</strong> signs you out and asks for a new address — blocked while anything is still queued.</dd>
<dt>Log out</dt><dd>Returns to the login screen. The server address is kept.</dd>
</dl>
<figure class="figshot phone"><div class="figshot-imgs"><img src="img/shot-10.png" alt="More menu" loading="lazy"></div><figcaption><b>Figure A10</b> — More. Change PIN, the connected server, and Log out.</figcaption></figure>
</section>
<hr class="rule">
<!-- =============================== PART B =============================== -->
<section id="partB">
<h2 class="part">Part B — The ERPNext side</h2>
<h3>Who this part is for</h3>
<p>
The office. Everything the driver can and cannot do is decided here. You need
the <span class="chip">Van Manager</span> or
<span class="chip">System Manager</span> role.
</p>
</section>
<section id="b1">
<h4><span class="step">B1</span>Where everything lives</h4>
<p>
One workspace: <strong>Vansale</strong> in the desk sidebar, or go straight to
<code>/app/vansale</code>.
</p>
<figure>
<div class="frame wide">
<svg viewBox="0 0 720 300" role="img" aria-label="Vansale workspace layout: cards for Today, Performance, Setup, Customer Routing, Transactions, Stock, Reports">
<rect width="720" height="300" fill="var(--paper)"/>
<text x="24" y="30" font-family="var(--sans)" font-size="13" font-weight="700" fill="var(--ink)">Vansale</text>
<text x="24" y="58" font-family="var(--sans)" font-size="9" font-weight="700" fill="var(--muted)">TODAY</text>
<g font-family="var(--sans)" font-size="9" fill="var(--ink-2)">
<rect x="24" y="66" width="156" height="40" rx="6" fill="var(--surface)" stroke="var(--line)"/><text x="34" y="90">Sales Today</text>
<rect x="190" y="66" width="156" height="40" rx="6" fill="var(--surface)" stroke="var(--line)"/><text x="200" y="90">Invoices Today</text>
<rect x="356" y="66" width="156" height="40" rx="6" fill="var(--surface)" stroke="var(--line)"/><text x="366" y="90">Collection Today</text>
<rect x="522" y="66" width="174" height="40" rx="6" fill="var(--surface)" stroke="var(--line)"/><text x="532" y="90">Outstanding</text>
</g>
<text x="24" y="132" font-family="var(--sans)" font-size="9" font-weight="700" fill="var(--muted)">PERFORMANCE</text>
<rect x="24" y="140" width="330" height="52" rx="6" fill="var(--surface)" stroke="var(--line)"/>
<polyline points="40,180 80,168 120,174 160,152 200,160 240,142 280,150 330,136" fill="none" stroke="var(--accent)" stroke-width="2"/>
<rect x="364" y="140" width="160" height="52" rx="6" fill="var(--surface)" stroke="var(--line)"/>
<g fill="var(--accent)"><rect x="378" y="168" width="14" height="18"/><rect x="400" y="158" width="14" height="28"/><rect x="422" y="164" width="14" height="22"/><rect x="444" y="150" width="14" height="36"/></g>
<rect x="534" y="140" width="162" height="52" rx="6" fill="var(--surface)" stroke="var(--line)"/>
<circle cx="580" cy="166" r="17" fill="none" stroke="var(--accent)" stroke-width="6"/>
<text x="606" y="170" font-family="var(--sans)" font-size="9" fill="var(--ink-2)">by mode</text>
<g font-family="var(--sans)" font-size="9">
<text x="24" y="220" font-size="9" font-weight="700" fill="var(--muted)">SETUP</text>
<text x="24" y="236" fill="var(--accent)">Open Van Sale App</text>
<text x="24" y="250" fill="var(--ink-2)">Vansale Configuration</text>
<text x="24" y="264" fill="var(--ink-2)">Vansale Settings</text>
<text x="24" y="278" fill="var(--ink-2)">Vansale Pin · Outbox</text>
<text x="250" y="220" font-size="9" font-weight="700" fill="var(--muted)">ROUTING</text>
<text x="250" y="236" fill="var(--ink-2)">Van Customer Assignment</text>
<text x="250" y="250" fill="var(--ink-2)">Van Driver Roster</text>
<text x="250" y="264" fill="var(--ink-2)">Van Daily Visit · Visit Log</text>
<text x="486" y="220" font-size="9" font-weight="700" fill="var(--muted)">REPORTS</text>
<text x="486" y="236" fill="var(--ink-2)">Van Daily Settlement</text>
<text x="486" y="250" fill="var(--ink-2)">Van Collection Summary</text>
<text x="486" y="264" fill="var(--ink-2)">Van Stock Position</text>
<text x="486" y="278" fill="var(--ink-2)">Van Visit Compliance</text>
</g>
</svg>
</div>
<figcaption><b>Figure B1</b> — the Vansale workspace. Number cards and charts first, then the setup links, then reports.</figcaption>
</figure>
<figure class="figshot desk multi"><div class="figshot-imgs"><img src="img/shot-11.png" alt="Vansale workspace" loading="lazy"><img src="img/shot-17.png" alt="Van Sales dashboard" loading="lazy"></div><figcaption><b>Figure B1</b> — the Vansale workspace, and the Van Sales dashboard.</figcaption></figure>
</section>
<section id="b2">
<h4><span class="step">B2</span>Vansale Settings — the global switches</h4>
<p>
<code>/app/vansale-settings</code>. Three master switches for the whole site.
Every one can be overridden per van, and PIN can be overridden per user.
</p>
<div class="tw">
<table>
<thead><tr><th>Setting</th><th class="f-cell">Field</th><th>Default</th><th>Effect when off</th></tr></thead>
<tbody>
<tr>
<td><strong>Enable route planning</strong></td>
<td class="f-cell">enable_route</td>
<td><span class="chip on">on</span></td>
<td>Route disappears from the app. The fourth tab and the fourth home card become <strong>Van Stock</strong>. Route screens are unreachable even by deep link.</td>
</tr>
<tr>
<td><strong>Require PIN unlock</strong></td>
<td class="f-cell">require_pin</td>
<td><span class="chip on">on</span></td>
<td>No PIN is set or asked for. Drivers go straight to Home after login, and Change PIN is hidden.</td>
</tr>
<tr>
<td><strong>Allow UOM change on invoice lines</strong></td>
<td class="f-cell">allow_uom_change</td>
<td><span class="chip on">on</span></td>
<td>Every line sells in the item's stock UOM. The unit selector is hidden.</td>
</tr>
</tbody>
</table>
</div>
<div class="note">
<span class="tag">When changes reach the phone</span>
<p>
On the driver's next app start, or when they switch back to the app from the
background, or when signal returns. No re-login needed. On builds older than
v1.0.29 the driver must log out and back in.
</p>
</div>
<figure class="figshot desk"><div class="figshot-imgs"><img src="img/shot-12.png" alt="Vansale Settings" loading="lazy"></div><figcaption><b>Figure B2</b> — the three global switches. Each can be overridden per van; PIN can also be overridden per user.</figcaption></figure>
</section>
<section id="b3">
<h4><span class="step">B3</span>Vansale Configuration — one record per van</h4>
<p>
<code>/app/vansale-configuration</code>. This is the van. It defines who drives
it, which warehouse it draws from, how its documents are numbered, and which
modules its drivers see.
</p>
<div class="tw">
<table>
<thead><tr><th>Field</th><th class="f-cell">Name</th><th>Notes</th></tr></thead>
<tbody>
<tr><td><strong>Van Code</strong></td><td class="f-cell">van_code</td><td>Required. The record's identity, e.g. <code>VAB-AB-001</code>. Appears in the app header and every report.</td></tr>
<tr><td><strong>Van Name</strong></td><td class="f-cell">van_name</td><td>Friendly label, e.g. <em>Dammam Van</em>.</td></tr>
<tr><td><strong>Company</strong></td><td class="f-cell">company</td><td>Required. Warehouses and cost centres must belong to it — the form refuses mismatches.</td></tr>
<tr><td><strong>Branch</strong></td><td class="f-cell">branch</td><td>Optional. Grants the driver a Branch user permission.</td></tr>
<tr><td><strong>Route planning</strong></td><td class="f-cell">route_mode</td><td><code>Follow Global</code> · <code>Enabled</code> · <code>Disabled</code>.</td></tr>
<tr><td><strong>PIN unlock</strong></td><td class="f-cell">pin_mode</td><td>Same three options. Applies to every driver on the van unless their own row overrides it.</td></tr>
<tr><td><strong>UOM change on invoice</strong></td><td class="f-cell">uom_change_mode</td><td>Same three options.</td></tr>
<tr><td><strong>Selling Price List</strong></td><td class="f-cell">selling_price_list</td><td>Van-level pricing tier. See B6.</td></tr>
<tr><td><strong>Capture location on visit end</strong></td><td class="f-cell">require_location</td><td>Off by default. On, the app requests GPS permission and tags visit completion with coordinates.</td></tr>
<tr><td><strong>Document Prefix</strong></td><td class="f-cell">doc_prefix</td><td>Per-van numbering. See B8.</td></tr>
<tr><td><strong>Naming Series</strong></td><td class="f-cell">naming_series_table</td><td>Read-only. Generated from the prefix on save.</td></tr>
</tbody>
</table>
</div>
<figure class="figshot desk"><div class="figshot-imgs"><img src="img/shot-13.png" alt="Vansale Configuration record" loading="lazy"></div><figcaption><b>Figure B3</b> — one van. Modules, pricing, document numbering, warehouses and assigned users.</figcaption></figure>
</section>
<section id="b4">
<h4><span class="step">B4</span>Assigned Users — who drives this van</h4>
<p>
The <strong>Assigned Users</strong> table is what connects a person to a van.
Adding a row here does the work for you on save: it creates the Company,
Branch, Warehouse and Cost Center user permissions and grants the role.
Removing a row takes them away again.
</p>
<div class="tw">
<table>
<thead><tr><th>Column</th><th class="f-cell">Name</th><th>Notes</th></tr></thead>
<tbody>
<tr><td><strong>User</strong></td><td class="f-cell">user</td><td>The ERPNext user who signs in on the phone.</td></tr>
<tr><td><strong>Role</strong></td><td class="f-cell">role</td><td><code>Van User</code> for a driver, <code>Van Manager</code> for a supervisor. Granted automatically.</td></tr>
<tr><td><strong>Sales Person</strong></td><td class="f-cell">sales_person</td><td>Tagged onto every invoice for commission, and it is how the driver's customer list is scoped.</td></tr>
<tr><td><strong>PIN unlock</strong></td><td class="f-cell">pin_mode</td><td>Per-driver override. The most specific setting in the whole system.</td></tr>
</tbody>
</table>
</div>
<div class="note warn">
<span class="tag">One van per user</span>
<p>
A user belongs to one van. Listing the same person on two vans makes their
van attribution ambiguous — reports resolve the van from the person who
created the document, so a second row makes the numbers unreliable.
</p>
</div>
</section>
<section id="b5">
<h4><span class="step">B5</span>Warehouse and cost centre</h4>
<p>
<strong>The first row is the default.</strong> The first warehouse becomes the
driver's van warehouse — the one their catalog is filtered by and stock is
sold out of. Add more rows only if the van genuinely draws from more than one.
Same rule for cost centres.
</p>
<div class="note">
<span class="tag">This is the single most common setup mistake</span>
<p>
Rows in the wrong order means the driver sells from the main store instead of
the van, and van stock reports show nothing. Check the order, not just the
contents.
</p>
</div>
</section>
<section id="b6">
<h4><span class="step">B6</span>Pricing — which price list wins</h4>
<p>First match wins, top to bottom:</p>
<div class="chain">
<div class="lnk">Customer<b>default_price_list</b></div>
<span class="arrow" aria-hidden="true">→</span>
<div class="lnk">Van<b>selling_price_list</b></div>
<span class="arrow" aria-hidden="true">→</span>
<div class="lnk">Customer Group<b>default_price_list</b></div>
<span class="arrow" aria-hidden="true">→</span>
<div class="lnk">Selling Settings<b>site default</b></div>
</div>
<p>
The van sits <em>above</em> Customer Group on purpose: a van running a
discounted route should not fall back to the group's sticker price when it
reaches a customer nobody has classified.
</p>
<p>
Rates are fetched per <em>item and unit</em>, so a Box price and a Pcs price
are both honoured. With no Item Price for the chosen unit, the stock-unit price
is multiplied by the conversion factor.
</p>
</section>
<section id="b7">
<h4><span class="step">B7</span>Tax</h4>
<p>
The rate is resolved the same way the desk form does it — the customer's
<strong>Tax Category</strong>, through <strong>Tax Rule</strong>, to a
<strong>Sales Taxes and Charges Template</strong>, falling back to the
company default. Nothing about tax is configured in Van Sale itself.
</p>
<div class="chain">
<div class="lnk">Customer<b>tax_category</b></div>
<span class="arrow" aria-hidden="true">→</span>
<div class="lnk">Tax Rule<b>match</b></div>
<span class="arrow" aria-hidden="true">→</span>
<div class="lnk">Template<b>rate + inclusive</b></div>
<span class="arrow" aria-hidden="true">→</span>
<div class="lnk">fallback<b>company default</b></div>
</div>
<p>
So a zero-rated or export customer is taxed correctly in the van, with no van
setting involved: give the customer the right Tax Category and the app follows.
</p>
<h4>Inclusive price lists</h4>
<p>
If a template's rows are marked <span class="chip">Included in Print Rate</span>,
the price already contains the tax. The app then <em>backs the tax out</em> of
the rate the driver typed instead of adding it on top, and the totals show
<span class="chip">incl.</span> with a <em>Net of tax</em> line.
</p>
<div class="note crit">
<span class="tag">Check this before going live</span>
<p>
Inclusive versus exclusive changes what the customer is charged. Post one
test invoice for a normal customer and one for a zero-rated customer, and
compare the app's totals against the saved document, before any real
selling. A wrong template overcharges by the full tax on every line.
</p>
</div>
<p>
Compound templates — anything using <em>On Previous Row</em> — cannot be
collapsed into one percentage. The app says the preview is approximate and
shows the server's exact figure after save.
</p>
</section>
<section id="b8">
<h4><span class="step">B8</span>Document numbering per van</h4>
<p>
Set <strong>Document Prefix</strong> on the van, e.g. <code>VAN1-</code>, and
save. The app then numbers that van's documents independently:
</p>
<div class="tw">
<table>
<thead><tr><th>Document</th><th class="f-cell">Series</th><th>Used for</th></tr></thead>
<tbody>
<tr><td>Sales Invoice</td><td class="f-cell">VAN1-INV-.YYYY.-.####</td><td>Normal sales</td></tr>
<tr><td>Sales Invoice</td><td class="f-cell">VAN1-CN-.YYYY.-.####</td><td>Credit notes / returns</td></tr>
<tr><td>Payment Entry</td><td class="f-cell">VAN1-PE-.YYYY.-.####</td><td>Collections</td></tr>
<tr><td>Stock Entry</td><td class="f-cell">VAN1-SE-.YYYY.-.####</td><td>Van loading and transfers</td></tr>
</tbody>
</table>
</div>
<p>
The prefix is upper-cased and given a single trailing dash automatically —
<code>van1</code> becomes <code>VAN1-</code>. Leave it blank and the van uses
the site's normal series.
</p>
<div class="note warn">
<span class="tag">Do not hand-edit the series rows</span>
<p>
Each series carries a per-document abbreviation — <code>INV</code>,
<code>CN</code>, <code>PE</code>, <code>SE</code> — and that is what keeps
the counters separate. Frappe counts by the resolved prefix, so if two
document types ever shared one template they would share one counter:
invoice 41 followed by receipt 42. The table is read-only for that reason.
</p>
</div>
<p>
Returns get their own abbreviation so credit notes never consume invoice