Tips and Tricks

Labels
AJAX(112) App Studio(9) Apple(1) Application Builder(245) Application Factory(207) ASP.NET(95) ASP.NET 3.5(45) ASP.NET Code Generator(72) ASP.NET Membership(28) Azure(18) Barcode(2) Barcodes(3) BLOB(18) Business Rules(1) Business Rules/Logic(140) BYOD(13) Caching(2) Calendar(5) Charts(29) Cloud(14) Cloud On Time(2) Cloud On Time for Windows 7(2) Code Generator(54) Collaboration(11) command line(1) Conflict Detection(1) Content Management System(12) COT Tools for Excel(26) CRUD(1) Custom Actions(1) Data Aquarium Framework(122) Data Sheet(9) Data Sources(22) Database Lookups(50) Deployment(22) Designer(178) Device(1) DotNetNuke(12) EASE(20) Email(6) Features(101) Firebird(1) Form Builder(14) Globalization and Localization(6) How To(1) Hypermedia(2) Inline Editing(1) Installation(5) JavaScript(20) Kiosk(1) Low Code(3) Mac(1) Many-To-Many(4) Maps(6) Master/Detail(36) Microservices(4) Mobile(63) Mode Builder(3) Model Builder(3) MySQL(10) Native Apps(5) News(18) OAuth(9) OAuth Scopes(1) OAuth2(13) Offline(20) Offline Apps(4) Offline Sync(5) Oracle(11) PKCE(2) Postgre SQL(1) PostgreSQL(2) PWA(2) QR codes(2) Rapid Application Development(5) Reading Pane(2) Release Notes(184) Reports(48) REST(29) RESTful(29) RESTful Workshop(15) RFID tags(1) SaaS(7) Security(81) SharePoint(12) SPA(6) SQL Anywhere(3) SQL Server(26) SSO(1) Stored Procedure(4) Teamwork(15) Tips and Tricks(87) Tools for Excel(3) Touch UI(93) Transactions(5) Tutorials(183) Universal Windows Platform(3) User Interface(338) Video Tutorial(37) Web 2.0(100) Web App Generator(101) Web Application Generator(607) Web Form Builder(40) Web.Config(9) Workflow(28)
Archive
Blog
Tips and Tricks
Friday, April 16, 2021PrintSubscribe
Say Goodbye to Glyphicons

 Touch UI has a long history. The framework was introduced in December of 2013. It was built on top of jQuery Mobile and represented our attempt to provide a “mobile” presentation style for apps created with Code On Time.

Here is the sample screenshot from that time:


The graphics of jQuery Mobile were based on a small subset of Glyphicons that was contributed by the namesake company in the SVG format. The extended set of 250 icons found their way into Bootstrap 3.0. We have integrated Bootstrap to provide the means of creating the content pages in Touch UI and promptly upgraded the framework to use the corresponding web font.


Glyphicons continued to be a part of Touch UI framework up until now.


Touch UI of today gets its looks from the Material Icons library and can be extended with an additional icon set when needed. The new framework works equally well on the devices driven by mouse and touch. It supports modern web browsers on any screen size. It can operate in native mode and as a part of a Progressive Web App.


Starting with the release 8.9.18.0 the glyphicons are not the standard component of Touch UI. New projects will not have these icons included. The legacy apps will retain the font in the ~/app/fonts folder but will require one of the following after being re-generated with the new release:

  • Replacing the references to the glyphicons with their  counterparts found in the Material Icons library.
  • Copying of the single CSS file from the code generation library into your own project.

The replacement of icons is simple. Here is how we did that for a few icons found in the current iteration of https://my.codeontime.com.


Either keep the span element of the icon definition or have it replaced with i tag. Replace the CSS classes in the icon definition with a single class material-icon. Enter the icon name between the opening and closing tag.


If you prefer to continue using the glyphicons, then copy and paste the glyphicons.css  file from the code generation library in [Code OnTime]/Library/Legacy/glyphicons folder into ~/app/css folder of your application. Run the app generator and click the “Locate” link below the list of projects to go straight to the library folder.

If you want to start using the glyphicons in the new projects then copy the other two files in the same folder and paste into the ~/app/fonts folder of your application.

Glyphicons are not widely used in the apps created with Code On Time. You may find the references to the icons in the dedicated login page of the app or in the content pages based on the bootstrap. The new code generation library is now referencing the material icons instead. This reduces significantly the size of the framework CSS files and provides a faster first paint in the browsers and web views.

The content pages of the near future will be based on Display Flow technology. The new presentation technology is shared with the Kiosk UI and will have its own Live Visual Designer. You will see it in action in the new community forum and in the support help desk. Materials Icons are integrated in the Display Flow and provide an excellent replacement for the Glyphicons.

Friday, May 15, 2020PrintSubscribe
Tutorial: Barcodes, QR codes, and RFID tags
Learn to create the Order Entry Form that works offline and online. The form will handle barcodes, QR codes, and RFID tags. The tutorial explains how to calculate the default values, extended price, and order total.


We start with the live demo of the app that will be created in this tutorial. Next we proceed to create the Order Entry Form shown in the demo and make it work in online and offline modes. The next iteration of the app is using UI Automation to implement the barcode processing for Product inventory, QR-code processing for Customer Loyalty Cards, RFID tags to handle Employee Access Cards. We bind it all together for Hands-Free Order Entry. The final part of the tutorial concentrates on creating the Kiosk-style presentation for the Order Entry Form.

Part 1 - Demo (30 minutes)

03:20 - Introduction
07:18 - Barcodes and Product Inventory
09:28 - QR codes and Customer Loyalty Cards
11:05 - RFID Access Cards and Employees
13:02 - Entering order line items with the barcode scanner
15:40 - Linking loyalty cards with the QR code scanner
16:51 - Linking employee to the order with the RFID reader
20:36 - Entering the lines items in “inventory” mode
24:38 - Kiosk presentation of Order Entry Form

Part 2 - Building Order Entry Form (1 hour 42 minutes)

0:32:49 - Introduction
0:33:16 - Database Diagram of Order Entry Form
0:35:58 - Configuring Sample Database
0:40:20 - Creating data models for Orders, Customers, Employees, and Shippers
0:48:52 - Creating data models for Order Details, Products, Categories, and Suppliers
0:54:36 - Modifying Menu
0:55:35 - Linking Order and Order Details
1:06:20 - Calculating default values with SQL business rules
1:09:08 - Calculating default values with JavaScript business rules
1:15:25 - Copying the Unit Price
1:18:24 - Calculating Extended Price with JavaScript
1:32:23 - Copying the shipping information from the Customer to the Order
1:37:14 - Calculating the Order Total
2:00:32 - Taking Orders in offline mode
2:12:25 - Syncing offline orders with the “online” database

Part 3 - Barcode Input and UI Automation (3 hours)

2:15:03 - Enabling the barcode input
2:26:45 - Simulating scanning of a barcode
2:33:20 - Low-level barcode processing
2:40:56 - Introducing UI Automation
2:42:33 - Integrating barcodes in the Product Inventory
3:20:09 - Integrating barcodes and QR-codes to handle Customer  Loyalty Cards
3:35:46 - Integrating RFID Access Cards to identify the Employees
3:46:55 - Using Data Queries during UI Automation
3:54:45 - Implementing Hands-Free Order Entry
5:08:22 - Taking orders in offline mode with barcodes, QR codes, and RFID tags

Part 4 - Kiosk UI (2 hours)

5:14:33 - Creating a Kiosk-style Order Entry presentation
6:11:55 - Adding Numeric Pad for parameter input
6:34:20 - Displaying date selector and pick list
6:43:35 - Making Order Entry Kiosk responsive
7:14:26 - Taking orders in the offline Kiosk

The source code produced in the tutorial is available at https://codeontime.com/blog/2020/05/hello-barcodes-sample-code.

The following equipment was used in the tutorial:

Inateck Bluetooth Barcode Scanner
https://www.amazon.com/gp/product/B074M6RTM3


Koolertron Wireless 2D Barcode Scanner
https://www.amazon.com/gp/product/B0716PQYJP


RFID Reader 125KHz
https://www.amazon.com/gp/product/B07TMNZPXK

Tuesday, May 12, 2020PrintSubscribe
"Hello Barcodes!" Sample Code
The tutorial "Barcodes, QR codes, and RFID tags" results in the production of the following code:

~/touch-settings.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
  "barcode": {
    "enabled": true
  },
  "ui": {
    "automation": {
      "enabled": true,
      "kiosk": {
        "enabled": true,
        "gapColor": true,
        "maxWidth": 2000,
        "copyright": false,
        "fullscreen":  false
      }
    }
  }
}

~/js/MyApp.js

  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
/*eslint eqeqeq: ["error", "smart"]*/

(function () {

    var Barcodes = {
        LoyaltyCard: /^lc/i,
        AccessCard: /^000/
    };

    // business rules

    $app.rules.Orders = {
        Calculate: function (dataView, args) {
            var freight = dataView.data().Freight;
            if (freight == null)
                freight = 0;

            $app.execute({ from: 'OrderDetails' }).then(function (result) {
                var total = 0;
                result.OrderDetails.forEach(function (detail) {
                    total += detail.ExtendedPrice;
                });
                if (total > 0)
                    total += freight;
                $app.input.execute({ OrderTotal: total });
            });
            return false;
        },
        after: {
            New: function (dataView, args) {
                var order = dataView.data();
                $app.input.execute({ OrderDate: new Date(), OrderNumber: order.OrderID });
            }
        }
    };

    $app.rules.OrderDetails = {
        after: {
            New: function (dataView, args) {
                $app.input.execute({ Discount: 0, Quantity: 1 });
            }
        },
        Calculate: function (dataView, args) {
            var details = dataView.data();

            var unitPrice = details.UnitPrice;
            if (unitPrice == null)
                unitPrice = 0;

            var quantity = details.Quantity;
            if (quantity == null)
                quantity = 0;

            var discount = details.Discount;
            if (discount == null)
                discount = 0;

            var extendedPrice = unitPrice * quantity * (1 - discount);
            $app.input.execute({ ExtendedPrice: extendedPrice })

            return false;
        }
    }

    // Barcode Input Simulator

    $(document).on('context.app', function (e) {
        e.context.push({
            hidden: true,
            text: 'Scan Product 1',
            shortcut: 'Ctrl+Shift+1',
            callback: () => $app.input.barcode('842776107183')
        });
        e.context.push({
            hidden: true,
            text: 'Toggle Kiosk',
            shortcut: 'Ctrl+Shift+K',
            callback: () => $app.touch.kiosk(!$app.touch.kiosk())
        });
    });

    // low-level barcode processing

    //$(document).on('barcode.app', function (e) {
    //    $app.touch.notify('I am handling this: ' + e.text);
    //    setTimeout(function () {
    //        $app.input.barcode(true);
    //    }, 7000)
    //    return false;
    //});

    //
    // Special Workflow Control Barcodes
    //

    $app.ifThisThenThat((app, context) =>
        app.if(context.barcode('ORDER_LINE_SETQTY').input('numpad.number', v => v > 0))
            .then(() => app.action('edit', 'details'))
            .then(() => app.val({
                'qua': context.input('numpad.number')
            }))
            .then(() => app.action('save'))
            .then(() => app.notify(false))
            .then(() => app.input('numpad', null))
    );

    $app.ifThisThenThat((app, context) =>
        app.if(context.barcode('ORDER_LINE_SETQTY'))
            .then(() => app.notify('Please use the numeric pad to enter the quantity.'))
    );

    $app.ifThisThenThat((app, context) =>
        app.if(context.barcode('ORDER_LINE_INC'))
            .then(() => app.action('edit', 'details'))
            .then(() => app.inc('qua'))
            .then(() => app.action('save'))
            .then(() => app.notify(false))
    );

    $app.ifThisThenThat((app, context) =>
        app.if(context.barcode('ORDER_LINE_DEC'))
            .then(() => app.action('edit', 'details'))
            .then(() => app.dec('qua'))
            .then(() => app.action('save'))
            .then(() => app.notify(false))
    );

    $app.ifThisThenThat((app, context) =>
        app.if(context.barcode('ORDER_LINE_5OFF'))
            .then(() => app.action('edit', 'details'))
            .then(() => app.val('disc', .05))
            .then(() => app.action('save'))
            .then(() => app.notify(false))
    );

    //
    // Hands-Free Order Entry
    //

    $app.ifThisThenThat((app, context) =>
        app.if(context.page('orders').list().barcode(Barcodes.LoyaltyCard).field('ord+num').action('new order'))
            .then(() => app.action('new order'))
            .then(() => app.val({
                'freight': 10.00,
                'via company': 'Federal Shipping',
                'loyalty': context.barcode()
            }))
            .then(() => app.notify('Please scan the order items.'))
    );

    $app.ifThisThenThat((app, context) =>
        app.if(context.page('orders').editing().barcode(Barcodes.LoyaltyCard).field('loyalty').val('loyalty', null))
            .then(() => app.val({
                'loyalty': context.barcode()
            }))
    );

    $app.ifThisThenThat((app, context) =>
        app.if(context.page('orders').editing().barcode(Barcodes.LoyaltyCard).field('loyalty'))
            .then(() => app.confirm('Assign a different loyalty card?'))
            .then(() => app.val({
                'loyalty': context.barcode()
            }))
    );

    $app.ifThisThenThat((app, context) =>
        app.if(context.page('orders').list().barcode(Barcodes.AccessCard).field('ord+num').action('new order'))
            .then(() => app.action('new order'))
            .then(() => app.val({
                'freight': 10.00,
                'via company': 'Federal Shipping',
                'access': context.barcode()
            }))
            .then(() => app.notify('Please scan the order items.'))
    );

    $app.ifThisThenThat((app, context) =>
        app.if(context.page('orders').editing().barcode(Barcodes.AccessCard).field('access'))
            .then(() => app.val({
                'access': context.barcode()
            }))
    );

    //
    // Product Barcode is scanned in Order Form
    //

    function tomorrow() {
        var d = new Date();
        d.setDate(d.getDate() + 1);
        return d;
    }

    $app.ifThisThenThat((app, context) =>
        app.if(context.page('orders').list().barcode().field('ord+num').action('new order'))
            .then(() => app.action('new order'))
            .then(() => app.val({
                'freight': 10.00,
                'via company': 'Federal Shipping',
                'required': tomorrow()
            }))
            .then(() => app.action('new order details', 'order details'))
            .then(() =>
                app.val({
                    'barcode': context.barcode(),
                    'qua': 0
                })
                    .fail(() =>
                        app.discard('Product not found.'))
            )
            .then(() => app.inc('qua', context.barcode()))
            .then(() => app.action('save'))
            .then(() => app.notify(false))
    );

    $app.ifThisThenThat((app, context) =>
        app.if(context.page('orders').editing().barcode().field('ord+num').row('barcode', context.barcode(), 'details'))
            .then(() => app.row('barcode', context.barcode(), 'details'))
            .then(() => app.action('edit', 'details'))
            .then(() => app.inc('qua', context.barcode()))
            .then(() => app.action('save'))
            .then(() => app.notify(false))
    );

    $app.ifThisThenThat((app, context) =>
        app.if(context.page('orders').editing().barcode().field('ord+num'))
            .then(() => app.action('new', 'details'))
            .then(() =>
                app.val({
                    'barcode': context.barcode(),
                    'qua': 0
                })
                    .fail(() =>
                        app.discard('Product not found.'))
            )
            .then(() => app.inc('qua', context.barcode()))
            .then(() => app.action('save'))
            .then(() => app.notify(false))
    );

    //
    // Using $app.execute to read data
    //

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.not().page('employee').barcode(Barcodes.AccessCard))
                .then(() =>
                    $app.execute({
                        controller: 'Employees',
                        filter: { AccessCard: context.barcode().unwrap() },
                        as: 'emp'
                    })
                        .then((result) =>
                            result.emp.length > 0 ?
                                app.notify(result.emp[0].FirstName + ' ' + result.emp[0].LastName +
                                    ', please scan the access card in the order form.') :
                                app.notify('Unknown access card: ' + context.barcode())
                        )
                )
    );

    //
    // Product Inventory
    //

    $app.ifThisThenThat((app, context) =>
        app.if(context.page('products').list().barcode().field('barcode').row('barcode', context.barcode()))
            .then(() => app.row('barcode', context.barcode()))
    );

    $app.ifThisThenThat((app, context) =>
        app.if(context.page('products').list().barcode().field('barcode'))
            .then(() => app.notify('Please select a product to assign a barcode.'))
    );

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.page('products').editing().barcode().field('barcode'))
                .then(() => app.val('barcode', context.barcode()))
    );

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.page('products').reading().field('barcode').barcode())
                .then(() =>
                    app.confirm('Assign the barcode ' + context.barcode() + ' to the product "' +
                        context.val('product') + '"?'))
                .then(() =>
                    app.action('edit'))
                .then(() =>
                    app.val('barcode', context.barcode()))
                .then(() =>
                    app.action('save'))
    );

    //
    // Customer Loyalty Cards
    //

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.page('customer').list().barcode(Barcodes.LoyaltyCard).field('loyalty').row('loyalty', context.barcode()))
                .then(() => app.rowFilter('loyalty', context.barcode()))
    );

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.page('customer').list().barcode(Barcodes.LoyaltyCard).field('loyalty'))
                .then(() => app.filter('loyalty', context.barcode()))
                .then(() => app.notify('Loyalty card not found: ' + context.barcode()))
    );

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.page('customer').editing().barcode(Barcodes.LoyaltyCard).field('loyalty'))
                .then(() => app.val('loyalty', context.barcode()))
    );

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.page('customer').reading().field('loyalty').barcode(Barcodes.LoyaltyCard))
                .then(() => app.confirm('Assign the loyalty card ' + context.barcode() + ' to the customer "' +
                    context.val('company') + '"?'))
                .then(() => app.action('edit'))
                .then(() => app.val('loyalty', context.barcode()))
                .then(() => app.action('save'))
    );

    //
    // Employee Access Cards
    //

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.page('employee').list().barcode(Barcodes.AccessCard).field('access').row('access', context.barcode()))
                .then(() => app.rowFilter('access', context.barcode()))
    );

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.page('employee').list().barcode(Barcodes.AccessCard).field('access'))
                .then(() => app.filter('access', context.barcode()))
                .then(() => app.notify('Access card not found: ' + context.barcode()))
    );

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.page('employee').editing().barcode(Barcodes.AccessCard).field('access'))
                .then(() => app.val('access', context.barcode()))
    );

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.page('employee').reading().field('access').barcode(Barcodes.AccessCard))
                .then(() => app.confirm('Assign the access card ' + context.barcode() + ' to the employee "' +
                    context.val('Last') + '"?'))
                .then(() => app.action('edit'))
                .then(() => app.val('access', context.barcode()))
                .then(() => app.action('save'))
    );

    var ordersDisplay2 = [
        { flow: 'row' },
        { flow: 'row' },
        {
            flow: 'column',
            label: 'Lines',
            in: 'details',
            count: 50,
            height: 'fill',
            alter: {
                _md: {
                    tab: 'Lines'
                }
            },
            '>': {},
            '#': {},
            'Product Name': {},
            'price': {
                alter: {
                    _lg: {
                        hidden: true
                    }
                }
            },
            'qua': { label: 'Qty' },
            'Disc': {
                label: 'Disc',
                alter: {
                    _lg: {
                        hidden: true
                    }
                }
            },
            'ext+price': { label: 'Ext. Price' },
            'barcode': { flow: 'row', label: false, width: '30%', wrap: false },
            'price2': {
                clone: 'price',
                flow: 'column',
                label: false,
                hidden: true,
                alter: {
                    _lg: {
                        hidden: null
                    }
                }
            },
            'supplier': {
                flow: 'column', label: false, wrap: true,
                alter: {
                    _lg: {
                        hidden: true
                    }
                }
            },
            'category': { flow: 'column', label: false, align: 'right' }
        },
        {
            alter: {
                _md: {
                    hidden: true
                }
            }
        },
        {
            alter: {
                _md: {
                    tab: 'Ship',
                    height: 'fill'
                }
            },
            'order date': { label: 'Ordered' },
            'required': { label: 'Required' },
            'ship via': {},
            'ship+phone': { label: 'Phone' },
            'salesPerson': {
                flow: 'column',
                expression: order => order.val('first') + ' ' + order.val('last'),
                when: order => order.val('last') != null,
                label: 'Employee',
                accentValue: true,
                alter: {
                    _md: {
                        flow: null
                    }
                }
            },
            'freight': {},
            'total': {
                flow: 'row',
                largeValue: true,
                colorValue: 'green',
                alter: {
                    _md: {
                        hidden: true
                    }
                }
            }
        },
        {
            flow: 'column',
            alter: {
                _md: {
                    hidden: true
                }
            }
        },
        {
            flow: 'column',
            label: 'Customer',
            width: '4x',
            height: 'fill',
            alter: {
                _md: {
                    flow: null,
                    tab: 'Customer',
                    height: 'fill'
                }
            },
            'ship name': {},
            'company': { label: false, medium: true, accent: true },
            'cust+ph': { label: 'Phone' },
            'loyalty': { label: 'Loyalty Card' },
            'ship addr': { label: false, dense: true },
            'city, region, zip': {
                expression: order =>
                    order.val('city') ?
                        order.val('city') + ', ' + (order.val('region') || '') + ' ' + order.val('postal') :
                        null,
                label: false,
                dense: true
            },
            'country': { label: false, dense: true }
        },
        {
            'total': {
                flow: 'row',
                largeValue: true,
                colorValue: 'green',
                hidden: true,
                alter: {
                    _md: {
                        hidden: null
                    }
                }
            }
        },
        {
            alter: {
                _md: {
                    hidden: true
                }
            }
        },
        {
            'numpad': {
                label: 'Search products or enter quantity',
                alter: {
                    _md: {
                        hidden: true,
                        compact: true
                    }
                }
            }
        },
        { flow: 'column' },
        {
            flow: 'column',
            width: '4x',
            height: 'fill',
            alter: {
                _sm: {
                    width: '2x'
                }
            },
            'save': {
                alter: {
                    _sm: {
                        text: false
                    }
                }
            },
            'cancel': {
                alter: {
                    _sm: {
                        text: false
                    }
                }
            }
        },
        {
            in: 'details',
            append: true,
            'Set quantity': {
                dark: true,
                barcode: 'ORDER_LINE_SETQTY',
                when: line => line.val('prod')
            },
            'delete': {
                darkest: true,
                text: false,
                wide: true
            }
        },
        {
            in: 'details',
            append: true,
            'Archive': {
                text: false,
                icon: 'material-icon-archive',
                when: line => line.val('prod'),
                barcode: 'ORDER_LINE_ARCHIVE'
            },
            'Increase Quantity': {
                text: false,
                dark: true,
                icon: 'material-icon-add',
                when: line => line.val('prod'),
                barcode: 'ORDER_LINE_INC'
            },
            'Decrease Quantity': {
                text: false,
                dark: true,
                icon: 'material-icon-remove',
                when: line => line.val('prod'),
                barcode: 'ORDER_LINE_DEC'
            },
            'Numeric Pad': {
                icon: 'material-icon-dialpad',
                text: false,
                darkest: true,
                perform: {
                    show: 'numpad',
                    hide: 'numpad'
                },
                hidden: true,
                alter: {
                    _md: {
                        hidden: null
                    }
                }
            }
        },
        {
            append: true,
            'Pay with credit card': {
                darkest: true,
                perform: {
                    pick: 'ord+date'
                }
            },
            'No sale': {
                dark: true,
                perform: {
                    pick: 'cust+comp'
                }
            }
        },
        {
            in: 'details',
            append: true,
            '5% discount': {
                wide: true,
                barcode: 'ORDER_LINE_5OFF',
                when: detail => detail.val('prod'),
                alter: {
                    _md: {
                        wide: false
                    }
                }
            }
        },
        { flow: 'column' },
        { flow: 'row' }
    ];

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.page('Orders').field('order number').editing().not().display(ordersDisplay2))
                .then(() => app.display(ordersDisplay2))
    );

    var ordersDisplay1 = 'Please scan a loyalty card or a product barcode to start your order.';

    $app.ifThisThenThat(
        (app, context) =>
            app.if(context.page('Orders').list().field('order number').not().display(ordersDisplay1))
                .then(() => app.display(ordersDisplay1))
    );




})();