Langsung ke konten utama

BELAJAR PYTHON : MENDESAIN NOTA KASIR #2 | APLIKASI KASIR POS ANDROID IOS MUDAH

 

SESI 08 : MENDESAIN NOTA KASIR #2

Assalamualaikum warahmatullohi wabarohatuh, salam sukses. Pada bagian ini kita akan membuat atau merancang form untuk input penjualan kasir atau form pos, untuk part atau bagian ke #2 ini kita akan membuat display nota penjualan kasir yang kita interaktifkan dengan display ambil menu item barang atau daftar menu barang yang dari part atau bagian #1 kemarin, adapun video tutorialnya secara lengkap dan detailnya ada di sini:





Dalam pengaturan display nota pembayaran kasir ini kita akan membuat class baru yaitu class PosCashier dan class PosGrid sebagai pengontrol tampilan layarnya yang diatur dalam file kivy, berarti kita juga membuat file kivy baru yaitu  pos_cashir.kv dan pos_grid.kv, kita juga harus melakukan penyesuaian terhadap file kivy menu.kv, pos_item.kv karena akan dipanggil juga oleh class PosCashier ini, selain itu kita juga harus melakukan penyesuaian di class utama yaitu class MyResto. Selain itu kita juga harus membuat tabel baru ntuk menyimpan data data penjualannya, baik untuk rancangan tampilan dan source scrip mengenai  perubahan-perubahan secara lengkap kita jabarkan di bawah ini.

Rancangan display nota kasir dibagi dalam 6 (enam) bagian yaitu:

01. Judul Nota 



02. Kop Nota

03. Header Rincian Barang

04. Detail Rincian Barang

05. Subtotal Penjualan

06. Tombol Menu-menu


Modul atau library python baru yang dibutuhkan adalah : import datetime  yaitu untuk pengoperasian tanggal dan waktu, tapi disini nanti yang kita butuhkan atau yang kita pakai hanyalah untuk menampilkan tanggal hari ini.

Tabel baru yang harus dibuat untuk menyimpan data penjualan adalah jual_nota dan jual_barang, disini kita harus merubah fungsi atau metod buat_database menjadi:

def buat_database():
    conn = sqlite3.connect("data_resto.db")
    mycursor = conn.cursor()
   
    try:
        mycursor.execute("select * from barang ")
    except:  
        queri = "CREATE TABLE IF NOT EXISTS barang("\
                "kode INTEGER PRIMARY KEY AUTOINCREMENT,"\
                "jenis TEXT DEFAULT '',"\
                "nama TEXT DEFAULT '',"\
                "harga INTEGER  DEFAULT 0,"\
                "gambar TEXT DEFAULT '',"\
                "status TEXT DEFAULT '') "  
        mycursor.execute(queri)    
        conn.commit()

    # tabel untuk rekap nota jual
    jualnota = "CREATE TABLE IF NOT EXISTS jual_nota("\
            "nojual TEXT DEFAULT '',"\
            "tanggal TEXT DEFAULT '',"\
            "meja TEXT DEFAULT '',"\
            "item INTEGER  DEFAULT 0,"\
            "jumlah INTEGER  DEFAULT 0,"\
            "bayar INTEGER  DEFAULT 0,"\
            "sisa INTEGER  DEFAULT 0,"\
            "kasir TEXT DEFAULT '') "  
    mycursor.execute(jualnota)    
    conn.commit()    

    # tabel untuk rincian barang nota
    jualbrg = "CREATE TABLE IF NOT EXISTS jual_barang("\
            "nojual TEXT DEFAULT '',"\
            "kode INTEGER  DEFAULT 0,"\
            "qty INTEGER  DEFAULT 0,"\
            "harga INTEGER  DEFAULT 0,"\
            "jumlah INTEGER  DEFAULT 0 ) "  
    mycursor.execute(jualbrg)    
    conn.commit()  

    conn.close()



Class baru yang kita buat adalah class PosCashier source codenya: 

class PosCashier(Screen):
    notajual = StringProperty ("")

    def on_enter(self):
        self.ids.notagrid.clear_widgets()
        self.tampil_barang()  
        return self
   
    def tampil_barang(self):
        carinojual = '*' if self.notajual.strip()=='' else self.notajual.strip()

        conn = sqlite3.connect('data_resto.db')
        conn.row_factory = sqlite3.Row
        cur = conn.cursor()

        # baca rincian barang
        sql = "SELECT x.*,y.nama "\
            "FROM jual_barang x "\
            "INNER JOIN barang y on x.kode=y.kode "\
            "WHERE x.nojual='"+carinojual+"' "
        cur.execute(sql)
        jual_barang = cur.fetchall()

        # baca rekap nota
        sql = "SELECT * FROM jual_nota WHERE nojual='"+carinojual+"' "
        cur.execute(sql)
        jual_nota = cur.fetchall()

        conn.commit()
        conn.close()

        if len(jual_nota)==0 :  
            bayar = 0
            self.ids.notabaru.text='(*)'
            self.ids.notanomor.text = ''
            self.ids.notatanggal.text = ''
            self.ids.notameja.text = ''
        else:
            bayar = jual_nota[0]['bayar']
            self.ids.notabaru.text=''
            self.ids.notanomor.text = jual_nota[0]['nojual']
            self.ids.notatanggal.text = jual_nota[0]['tanggal']
            self.ids.notameja.text = jual_nota[0]['meja']

        total = 0
        for record in jual_barang:
            itemjual=PosGrid()
            itemjual.nama = record['nama']
            itemjual.harga = str(record['qty']) +" x "+ str(record['harga'])
            itemjual.jumlah = str(record['jumlah'])
            total = total + record['jumlah']
            self.ids.notagrid.add_widget(itemjual)
       
        sisa = 0 if total>bayar else bayar-total
        self.ids.notajumlah.text = str(total)
        self.ids.notabayar.text = str(bayar)
        self.ids.notasisa.text = str(sisa)
        return self
   
    def additem(self):
        dibayar = 0 if self.ids.notabayar.text.strip()=='' else int(self.ids.notabayar.text)
        if self.ids.notanomor.text.strip()=='' or self.ids.notameja.text.strip()=='' or dibayar>0 :
            tombol_no = MDFlatButton(text="canc", on_release=lambda x:self.dialog.dismiss())
            self.dialog = MDDialog(title="Info",
                            text='Maaf Nomor jual/meja belum diisikan atau sudah dibayar ',
                            radius=[20, 7, 20, 7],
                            buttons=[tombol_no])
            self.dialog.open()
        else:
            # kalau data baru simpan nomor jual
            if self.ids.notabaru.text.strip()=='(*)':
                xnojual = self.ids.notanomor.text
                xtanggal= self.ids.notatanggal.text
                xmeja = self.ids.notameja.text
                conn = sqlite3.connect('data_resto.db')
                conn.row_factory = sqlite3.Row
                cur = conn.cursor()
                cur.execute("INSERT INTO jual_nota (nojual,tanggal,meja) VALUES (?,?,?)",(xnojual,xtanggal,xmeja))
                conn.commit()
                conn.close()

            PosItem.notajual=self.ids.notanomor.text
            self.manager.transition.direction = 'right'
            self.manager.current = 'PosItem'    

    def baru(self):
        self.ids.notagrid.clear_widgets()
        self.ids.notatanggal.text=f"{datetime.date.today()}"  
        self.ids.notanomor.text = ''
        self.ids.notameja.text=''
        self.ids.notabaru.text='(*)'
        self.ids.notajumlah.text=''
        self.ids.notabayar.text=''
        self.ids.notasisa.text=''

        # cari nomor terakhir
        conn = sqlite3.connect('data_resto.db')
        conn.row_factory = sqlite3.Row
        cur = conn.cursor()
        cur.execute("SELECT max(nojual) as nojual FROM jual_nota ")
        bacamota = cur.fetchall()
        conn.commit()
        conn.close()

        nomorakhir = bacamota[0]['nojual']
        if type(nomorakhir)!=str or nomorakhir=='None' or nomorakhir.strip()=='':
            self.ids.notanomor.text = '00001'
        else:
            nobaru = '00000' + str(int(nomorakhir)+1)
            self.ids.notanomor.text = nobaru[-5:]
   


Adapun file kivy baru sebagai penjabaran class PosCashier yaitu: pos_cashir.kv 

<PosCashier>:
    # Judul layar
    MDBoxLayout:
        size_hint: .9,.05
        pos_hint: {"x":.05, "y": .95}
        md_bg_color: rgba(0, 250, 0, 150)
        MDLabel:
            text:'NOTA INVOICE'
            font_size: '14dp'
            bold: True
            size_hint: .95,1
            halign: 'center'
        MDIconButton:
            icon: "select-group"
            icon_size: "20sp"
            pos_hint:{'x':.95, 'center_y':.5}
            on_release:
                root.manager.transition.direction='right'
                root.manager.current='MenuUtama'
   
    MDLabel:
        text:'Nomor Nota'
        font_size: '12dp'
        size_hint: .2,.1
        pos_hint: {'x':.05,'y':.88}
    MDTextField:
        id:notanomor
        font_size: '12dp'
        size_hint: .25,.1
        pos_hint: {"x":.25, "y": .88}
        disabled: True
    MDIconButton:
        icon: "text-search"
        pos_hint: {"x":.4, "y": .88}
        on_release: root.cari_nota()
   
    MDLabel:
        text:'Tanggal'
        font_size: '12dp'
        size_hint: .2,.1
        pos_hint: {'x':.55,'y':.88}
    MDTextField:
        id:notatanggal
        font_size: '12dp'
        size_hint: .25,.1
        pos_hint: {"x":.7, "y": .88}
        disabled: True
   
    MDLabel:
        text:'Meja ( a/n )'
        font_size: '12dp'
        size_hint: .2,.1
        pos_hint: {'x':.05,'y':.83}
    MDTextField:
        id:notameja
        font_size: '12dp'
        size_hint: .25,.1
        pos_hint: {"x":.25, "y": .83}
   
    MDLabel:
        id:notabaru
        text:''
        font_size: '12dp'
        size_hint: .1,.1
        pos_hint: {'x':.55,'y':.83}
   
    # header
    MDBoxLayout:
        size_hint: .9,.05
        pos_hint: {"x":.05, "y": .8}
        line_color: rgba(180, 190, 180, 255)
        md_bg_color: rgba(0, 250, 0, 150)
        orientation: 'horizontal'
        padding: 5

        MDLabel:
            text:'Nama Barang'
            font_size: '12dp'
            size_hint: .5,1
        MDLabel:
            text:'Harga'
            font_size: '12dp'
            size_hint: .25,1
        MDLabel:
            text:'Jumlah'
            font_size: '12dp'
            halign: 'right'
            size_hint: .2,1
   
    # rincian item
    MDBoxLayout:
        size_hint: .9,.6
        pos_hint: {"x":.05, "y": .2}
        line_color: rgba(180, 190, 180, 255)
        padding: 5

        ScrollView:
            size_hint_x: self.size_hint_x
            size_hint_y: self.size_hint_y
            do_scroll_x: False

            MDGridLayout:
                id:notagrid
                cols: 1
                row_default_height: "30dp"
                row_force_default: True
                adaptive_height: True
   
    # subtotal
    MDBoxLayout:
        size_hint: .9,.13
        pos_hint: {"x":.05, "y": .07}
        orientation: 'vertical'
        MDBoxLayout:
            MDLabel:
                text:'Total:'
                font_size: '12dp'
                size_hint_x: .7
                halign: 'right'
            TextInput:
                id:notajumlah
                font_size: "12dp"
                size_hint: .3,1
                halign: 'right'
                disabled: True
                disabled_foreground_color: 0,0,0,1
                background_disabled_normal: ''

        MDBoxLayout:
            MDLabel:
                text:'Bayar:'
                font_size: '12dp'
                size_hint_x: .7
                halign: 'right'
            TextInput:
                id:notabayar
                font_size: "12dp"
                size_hint: .3,1
                halign: 'right'
                disabled: True
                disabled_foreground_color: 0,0,0,1
                background_disabled_normal: ''

        MDBoxLayout:
            MDLabel:
                text:'Sisa:'
                font_size: '12dp'
                size_hint_x: .7
                halign: 'right'
            TextInput:
                id:notasisa
                font_size: "12dp"
                size_hint: .3,1
                halign: 'right'
                disabled: True
                disabled_foreground_color: 0,0,0,1
                background_disabled_normal: ''
   
    # footer layar
    MDBoxLayout:
        size_hint: .9,.05
        pos_hint: {"x":.05, "y": .0}
        md_bg_color: rgba(0, 250, 0, 255)

        MDRectangleFlatIconButton:
            text: 'Print'
            font_size: "11sp"
            text_color: 0, 0, 1, 1
            md_bg_color: rgba(0, 250, 0, 150)
            icon: "printer-pos"
            icon_color: rgba(0, 0, 255, 255)
            size_hint: .25,1
       
        MDRectangleFlatIconButton:
            text: 'Baru'
            font_size: "11sp"
            text_color: 1, 1, 0, 1
            md_bg_color: rgba(255, 0, 0, 150)
            icon: "post-outline"
            icon_color: rgba(255, 255, 0, 255)
            size_hint: .25,1
            on_release: root.baru()
       
        MDRectangleFlatIconButton:
            text: 'Bayar'
            font_size: "11sp"
            text_color: 0, 0, 0, 1
            md_bg_color: rgba(255, 255, 0, 150)
            icon: "cash-multiple"
            icon_color: rgba(0, 0, 0, 255)
            size_hint: .25,1
            on_release: root.posbayar()
       
        MDRectangleFlatIconButton:
            text: 'Item'
            font_size: "11sp"
            text_color: 1, 1, 1, 1
            md_bg_color: rgba(0, 0, 255, 150)
            icon: "plus-circle"
            size_hint: .25,1
            icon_color: rgba(255, 255, 255, 255)
            on_release: root.additem()


Class baru yang kita buat adalah class PosGrid source codenya: 

class PosGrid(MDBoxLayout):
    nama = StringProperty ("Nama Item Food 00001")
    harga = StringProperty ('25 x 10000')
    jumlah = StringProperty ("250000")


Adapun file kivy baru sebagai penjabaran class PosGrid yaitu: pos_grid.kv 

<PosGrid>:
    MDLabel:
        text:root.nama
        font_size: '12dp'
        size_hint: .5,1
    MDLabel:
        text:root.harga
        font_size: '12dp'
        size_hint: .25,1
    MDLabel:
        text:root.jumlah
        font_size: '12dp'
        halign: 'right'
        size_hint: .2,1


File kivi yang harus disempurnakan yaitu file menu.kv yang diubah khusus pada button untuk menjalankan men pos :

    MDBoxLayout:
        size_hint: .4,.3
        pos_hint: {'x':.05,'y':.5}
        md_bg_color:rgba(0, 255, 255, 150)
        radius: [10,10,10,10]
        line_color: rgba(0, 0, 0, 255)
        orientation: 'vertical'
        padding: '10dp'
        FitImage:
            source: 'image/menu-pos.png'
        MDRectangleFlatIconButton:
            icon: "cart-variant"
            text:'Poin Of Sales'
            text_color: rgba(0, 0, 0, 255)
            user_font_size: '20dp'
            md_bg_color:rgba(255, 255, 0, 255)
            size_hint_x: 1
            pos_hint: {"center_x": .5}
            on_release:
                root.manager.transition.direction= 'left'
                root.manager.current= 'PosCashier'


Class yang harus disesuaikan adalah class PosItem source codenya menjadi: 

class PosItem(Screen):
    notajual = StringProperty ("*")

    def on_pre_enter(self):
        self.ids.gridtab01.clear_widgets()
        self.ids.gridtab02.clear_widgets()
        self.ids.gridtab03.clear_widgets()
        self.tampil_barang()
        return self
       
    def tampil_barang(self):
        conn = sqlite3.connect('data_resto.db')
        conn.row_factory = sqlite3.Row
        cur = conn.cursor()
        carinojual = '*' if self.notajual.strip()=='' else self.notajual.strip()
        sql = "SELECT x.kode,x.jenis,x.nama,x.harga,x.status,x.gambar,sum(iif(y.nojual=?, y.qty, 0)) as qty "\
                "FROM barang x "\
                "LEFT JOIN jual_barang y on x.kode=y.kode "\
                "GROUP BY x.kode,x.jenis,x.nama,x.harga,x.status,x.gambar "\
                "ORDER BY x.jenis "
        cur.execute(sql,(carinojual,))
        daftarbarang = cur.fetchall()
        conn.commit()
        conn.close()
       
        for record in daftarbarang:
            item=BarangItem()
            item.nojual=self.notajual
            item.bawaclass = 'MenuPos'
            item.iconkiri="minus-circle"
            item.iconkanan="plus-circle"  
            item.kode=str(record['kode'])
            item.jenis='0' if self.notajual.strip()=='' else str(record['qty'])
            item.nama=record['nama']
            item.harga=str(record['harga'])
            item.gambar=record['gambar']
            item.status=record['status']
            if record['jenis']=='1': self.ids.gridtab01.add_widget(item)
            if record['jenis']=='2': self.ids.gridtab02.add_widget(item)
            if record['jenis']=='3': self.ids.gridtab03.add_widget(item)  
        return self
   
    def tekantab(self,tekan):
        self.ids.tab01.color=180/255, 190/255, 180/255, 255/255
        self.ids.tab02.color=180/255, 190/255, 180/255, 255/255
        self.ids.tab03.color=180/255, 190/255, 180/255, 255/255
        self.ids.tab01.font_size='14dp'
        self.ids.tab02.font_size='14dp'
        self.ids.tab03.font_size='14dp'

        if tekan==1:
            self.ids.tab01.color=1, 1, 1, 1
            self.ids.tab01.font_size='18dp'
        if tekan==2:
            self.ids.tab02.color=1, 1, 1, 1
            self.ids.tab02.font_size='18dp'
        if tekan==3:
            self.ids.tab03.color=1, 1, 1, 1
            self.ids.tab03.font_size='18dp'
   
    def done(self):
        PosCashier.notajual=self.notajual
        self.manager.transition.direction = 'left'
        self.manager.current = 'PosCashier'


File kivi penjabarannya yaitu file pos_item.kv yang diubah khusus pada button done:

    MDRectangleFlatButton:
        text: "Done"
        font_size: '18dp'
        valign: "center"
        text_color: 1, 1, 1, 1
        size_hint: 1,.05
        md_bg_color: rgba(0, 0, 255, 255)
        pos_hint: {'center_x': .5, 'y': .0}
        on_release: root.done()


Terakhir class utama yaitu class MyResto perlu penyesuaian menjadi:

class MyResto(MDApp):
    def build(self):
        buat_database()
        sm = ScreenManager()
        sm.add_widget(MenuUtama(name='MenuUtama'))        
        sm.add_widget(MenuBarang(name='MenuBarang'))
        sm.add_widget(BarangNew(name='BarangNew'))
        sm.add_widget(StokBarang(name='StokBarang'))
        sm.add_widget(PosItem(name='PosItem'))
        sm.add_widget(PosCashier(name='PosCashier'))
        return sm        
               
           
    def klik_ataskanan(self,bwkelas,bwkode,bwisi,bwnojual,bwharga):
        if bwkelas=='MenuBarang':
            BarangNew.datanew = 'edit'
            BarangNew.datakode = bwkode
            self.root.current = 'BarangNew'
            return bwisi
        elif bwkelas=='MenuPos':
            nilai=0 if bwisi.strip()=='' else int(bwisi)
            bwqty=nilai + 1
            self.simpan_item_jual(bwnojual,bwkode,bwqty,bwharga)
            return str(bwqty)

    def klik_ataskiri(self,bwkelas,bwkode,bwisi,bwnojual,bwharga):
        if bwkelas=='MenuBarang':
            tombol_ok = MDFlatButton(text="OK", on_release=lambda x:self.hapus_barang(bwkode))
            tombol_no = MDFlatButton(text="canc", on_release=lambda x:self.dialog.dismiss())
            self.dialog = MDDialog(title="Konfirmasi",
                            text='Apakah anda yakin hapus..?',
                            radius=[20, 7, 20, 7],
                            buttons=[tombol_ok, tombol_no])
            self.dialog.open()
            return bwisi
        elif bwkelas=='MenuPos':
            nilai=0 if bwisi.strip()=='' else int(bwisi)
            bwqty=nilai-1 if nilai>0 else 0
            self.simpan_item_jual(bwnojual,bwkode,bwqty,bwharga)
            return str(bwqty)
   
    def simpan_item_jual(self,bwnojual,bwkode,bwqty,bwharga):
        xnojual=bwnojual.strip()
        xkode=0 if bwkode.strip()=='' else int(bwkode)
        xharga=0 if bwharga.strip()=='' else int(bwharga)
        xqty=bwqty  
        xjumlah=xqty * xharga

        conn = sqlite3.connect('data_resto.db')
        cur = conn.cursor()
        # jika tidak jadi beli
        if xqty<=0:
            query_sql="DELETE FROM jual_barang WHERE kode=? and nojual=? "
            query_isi=(xkode,xnojual)
        else:
            cur.execute("SELECT kode,nojual FROM jual_barang WHERE kode=? and nojual=?",(xkode,xnojual))
            jualbarang = cur.fetchall()

            if len(jualbarang)==0:
                # berarti belum ada barang ini
                query_sql="INSERT INTO jual_barang (nojual,kode,harga,qty,jumlah) VALUES (?,?,?,?,?) "
                query_isi=(xnojual,xkode,xharga,xqty,xjumlah)  
            else:
                query_sql="UPDATE jual_barang set qty=?,jumlah=? WHERE kode=? and nojual=? "
                query_isi=(xqty,xjumlah,xkode,xnojual)
       
        cur.execute(query_sql,query_isi)
        conn.commit()
        conn.close()

    def hapus_barang(self,kode):
        self.dialog.dismiss()
        conn = sqlite3.connect('data_resto.db')
        cur = conn.cursor()
        cur.execute("DELETE FROM barang where kode='"+kode+"'")
        conn.commit()
        conn.close()
        self.root.current = 'MenuUtama'
        self.root.current = 'MenuBarang'
   
    def ubahstok(self,bwkode,bwstatus):
        if bwstatus=='kosong':
            statusbaru='Ada'
        else:
            statusbaru='kosong'
       
        conn = sqlite3.connect('data_resto.db')
        cur = conn.cursor()
        cur.execute("UPDATE barang set status=? where kode=?",(statusbaru,bwkode))
        conn.commit()
        conn.close()
        return statusbaru
   



Demikian untuk pengaturan tampilan menu struk nota kasir ini, untuk kelanjutannya silahkan buka dipostingan berikutnya atau lihat video berikutnya, semoga bermanfaat terima kasih wassalamualaikum warohmatullohi wabarohatuh.








Komentar

...