Commit 9c47ba81 by Owo Sugiana

Kali pertama

0 parents
*egg-info
build
test-*.ini
0.1 2020-05-06
--------------
- Kali pertama
Struktur Tabel PAD
==================
PAD adalah Pendapatan Asli Daerah mencakup pencatatan pajak reklame, air tanah,
dll. Dengan kata lain bukan PBB (Pajak Bumi dan Bangunan) maupun BPHTB (Bea
Perolehan Hak atas Tanah dan Bangunan).
Paket ini selain berisi struktur tabel juga memuat query yang kerap digunakan
untuk proses:
1. Inquiry (cek tagihan)
2. Payment (pembayaran)
3. Reversal (pembatalan pembayaran)
Uji Coba
--------
Buat file ``test-pad.ini`` seperti contoh berikut::
[main]
db_url = postgresql://sugiana:a@localhost/pad
persen_denda = 2
Sesuaikanlah pada ``db_url``. Kemudian dapatkan daftar tagihan::
$ ~/env/bin/simpad_available_invoice test-pad.ini
Nanti akan tampil seperti ini::
#1 20191***, 4110201 RESTORAN, PT. ***, Rp 27.392.721
#2 20191***, 4110201 RESTORAN, RM.***, Rp 712.600
#3 20191***, 4110201 RESTORAN, RUM***, Rp 4.415.397
Kolom kedua adalah Invoice ID atau sering disebut sebagai nomor bayar. Nomor
ini bekal untuk inquiry atau cek tagihan::
$ ~/env/bin/simpad_inquiry test-pad.ini --invoice-id=201912345
Untuk melanjutkan pembayaran tambahkan opsi ``--payment``::
$ ~/env/bin/simpad_inquiry test-pad.ini --invoice-id=201912345 --payment
Sedangkan untuk membatalkannya tambahkan opsi ``--reversal``::
$ ~/env/bin/simpad_inquiry test-pad.ini --invoice-id=201912345 --reversal
Selamat mencoba, semoga berhasil.
CREATE OR REPLACE FUNCTION pad.sms_bayar() RETURNS trigger
-- Irul, 15 Juni 2017
-- Owo, 6 Mei 2020
LANGUAGE plpgsql
AS $$
DECLARE
no_hp text;
BEGIN
--- SELECT trim(c.telphone), get_sptno(s.id)
SELECT c.telphone
INTO no_hp
FROM pad.pad_spt s
INNER JOIN pad.pad_customer c ON c.id = s.customer_id
WHERE s.id = NEW.spt_id;
no_hp = trim(no_hp);
IF (no_hp <> '') THEN
INSERT INTO im.kirim (penerima, pesan)
VALUES (no_hp, 'Terima Kasih Telah Membayar Pajak Daerah, Pajak Yang Anda Bayarkan Merupakan Wujud Partisipasi Anda Dalam Membangun Kabupaten Tangerang.');
END IF;
RETURN NEW;
END;
$$;
insert into pad_tp(id, singkatan, nama) values(8, 'MANDIRI', 'Bank Mandiri');
insert into pad_channel(id, nama) values (7014, 'Mitracomm');
[main]
db_url = postgresql://user:pass@localhost/db
persen_denda = 2
#rekening_notes =
File mode changed
File mode changed
import sys
from configparser import ConfigParser
from argparse import ArgumentParser
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
help_jenis = 'contoh: restoran'
def get_option(argv):
default_count = 10
help_count = 'default {}'.format(default_count)
pars = ArgumentParser()
pars.add_argument('conf')
pars.add_argument('--jenis', help=help_jenis)
pars.add_argument('--tahun', type=int)
pars.add_argument('--belum-jatuh-tempo', action='store_true')
pars.add_argument('--lewat-jatuh-tempo', action='store_true')
pars.add_argument(
'--count', type=int, default=default_count, help=help_count)
return pars.parse_args(argv)
def main(argv=sys.argv):
option = get_option(argv[1:])
conf = ConfigParser()
conf.read(option.conf)
module = __import__('opensipkd.pad.services')
AvailableInvoice = module.pad.services.AvailableInvoice
db_url = conf.get('main', 'db_url')
persen_denda = conf.getfloat('main', 'persen_denda')
engine = create_engine(db_url)
session_factory = sessionmaker(bind=engine)
module.pad.services.DBSession = session_factory()
a = AvailableInvoice(persen_denda, option)
a.show()
import sys
from datetime import datetime
from configparser import ConfigParser
from argparse import ArgumentParser
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
import transaction
from zope.sqlalchemy import register
from opensipkd.string.money import thousand
def show_val(label, value):
print('{}: {}'.format(label, value or ''))
def show_rp(label, value):
show_val(label, 'Rp {}'.format(thousand(value)))
def get_option(argv):
pars = ArgumentParser()
pars.add_argument('conf')
pars.add_argument('--invoice-id')
pars.add_argument('--payment', action='store_true')
pars.add_argument('--reversal', action='store_true')
return pars.parse_args(argv)
def show(inq):
show_val('Invoice ID', inq.invoice_id.get_raw())
show_val('Tahun Pajak', inq.get_tahun())
show_val('NPWPD', inq.get_npwp())
show_val('Nama Wajib Pajak', inq.get_nama())
show_val('Alamat Wajib Pajak', inq.get_alamat_wp())
show_val('Kode Pos Wajib Pajak', inq.get_kode_pos_wp())
show_val('Alamat Objek Pajak', inq.get_alamat_op())
show_val('Kelurahan Objek Pajak', inq.get_kelurahan_op())
show_val('Kecamatan Objek Pajak', inq.get_kecamatan_op())
show_rp('Tagihan', inq.tagihan)
show_rp('Denda', inq.denda)
show_rp('Total Bayar', inq.total_bayar)
show_rp('Total Tagihan', inq.total)
show_val('Is Available', inq.is_available())
def show_pkey_values(row):
print('Primary key tabel {}:'.format(row.__table__.name))
for c in row.__table__.columns:
if c.primary_key:
val = getattr(row, c.name)
show_val(' '+c.name, val)
def read_list_conf(conf, name):
return conf.has_option('main', name) and \
conf.get('main', name).split() or []
def error(s):
print(s)
sys.exit()
def main(argv=sys.argv):
option = get_option(argv[1:])
conf_file = option.conf
invoice_id = option.invoice_id
conf = ConfigParser()
conf.read(conf_file)
db_url = conf.get('main', 'db_url')
rekening_notes = read_list_conf(conf, 'rekening_notes')
engine = create_engine(db_url)
module = __import__('opensipkd.pad.services')
session_factory = sessionmaker(bind=engine)
module.pad.services.DBSession = session_factory()
register(module.pad.services.DBSession)
inq = module.pad.services.Inquiry(
invoice_id, rekening_notes, conf.getfloat('main', 'persen_denda'))
if not inq.invoice:
print('Invoice ID {} tidak ada.'.format(invoice_id))
return
show(inq)
if option.payment:
if not inq.total:
error('Tidak ada tagihan, tidak ada yang perlu dibayar.')
ntb = datetime.now().strftime('%y%m%d%H%M%S')
with transaction.manager:
pay = inq.do_payment(ntb)
show_pkey_values(pay)
print('Berhasil dibayar')
if option.reversal:
rev = module.pad.services.Reversal(invoice_id)
if rev.is_available():
error('Memang belum dibayar')
pay = rev.payment
if not pay:
error('Pembayaran tidak ditemukan, tidak ada yang perlu dibatalkan.')
with transaction.manager:
rev.do_reversal()
show_pkey_values(pay)
print('Berhasil dibatalkan')
from time import time
from textwrap import wrap
from datetime import datetime
from sqlalchemy import (
func,
exists,
and_,
)
from opensipkd.hitung import (
round_up,
hitung_denda,
)
from opensipkd.string import FixLength
from opensipkd.string.money import thousand
from .models import (
Kecamatan,
Kelurahan,
Customer,
CustomerUsaha,
Invoice,
Payment,
Pajak,
Rekening,
)
DBSession = None # Override, please
def get_db_session():
return DBSession
INVOICE_ID = [
('Tahun', 4, 'N'),
('SptNo', 5, 'N'),
]
class BaseInquiry:
def __init__(self, invoice_id, rekening_notes=[]):
self.invoice_id = FixLength(INVOICE_ID)
self.invoice_id.set_raw(invoice_id)
self.invoice = None
if not self.invoice_id['SptNo']:
return
self.rekening_notes = rekening_notes
q = DBSession.query(Invoice).filter_by(
tahun=self.invoice_id['Tahun'], sptno=self.invoice_id['SptNo'])
self.invoice = q.first()
def is_available(self):
return self.invoice.status_pembayaran == 0
def get_objek_pajak(self):
q = DBSession.query(CustomerUsaha).filter_by(
id=self.invoice.customer_usaha_id)
return q.first()
def get_wajib_pajak(self):
q = DBSession.query(Customer).filter_by(
id=self.objek_pajak.customer_id)
return q.first()
def get_kelurahan(self):
q = DBSession.query(Kelurahan).filter_by(
id=self.objek_pajak.kelurahan_id)
return q.first()
def get_kecamatan(self):
q = DBSession.query(Kecamatan).filter_by(
id=self.objek_pajak.kecamatan_id)
return q.first()
def get_pajak(self):
q = DBSession.query(Pajak).filter_by(id=self.invoice.pajak_id)
return q.first()
def get_rekening(self):
q = DBSession.query(Rekening).filter_by(id=self.pajak.rekening_id)
return q.first()
def set_profile(self):
self.objek_pajak = self.get_objek_pajak()
self.wajib_pajak = self.get_wajib_pajak()
self.pajak = self.get_pajak()
self.rekening = self.get_rekening()
self.kelurahan = self.get_kelurahan()
self.kecamatan = self.get_kecamatan()
if self.rekening.rekeningkd in self.rekening_notes:
alamat = wrap(self.invoice.notes, 40)
if alamat[1:]:
self.alamat1 = alamat[0]
self.alamat2 = alamat[1]
else:
self.alamat1 = self.wajib_pajak.alamat
self.alamat2 = alamat[0]
else:
alamat = wrap(self.wajib_pajak.alamat, 40)
self.alamat1 = alamat[0]
self.alamat2 = alamat[1:] and ' '.join(alamat[1:]) or ''
def get_tahun(self):
return self.invoice.tahun
def get_npwp(self):
return self.wajib_pajak.npwpd
def get_nama(self):
lengkap = []
if self.objek_pajak.opnm:
lengkap.append(self.objek_pajak.opnm)
nama = self.invoice.r_nama or self.wajib_pajak.customernm
if nama:
lengkap.append(nama)
return ','.join(lengkap)
def get_alamat_wp(self):
return self.wajib_pajak.alamat
def get_kode_pos_wp(self):
return self.wajib_pajak.wpkodepos
def get_alamat_1(self):
return self.alamat1
def get_alamat_2(self):
return self.alamat2
def get_alamat_op(self):
return self.objek_pajak.opalamat
def get_kelurahan_op(self):
return self.kelurahan.kelurahannm
def get_kecamatan_op(self):
return self.kecamatan.kecamatannm
def get_kode_rekening(self):
return self.rekening.rekeningkd
def get_nama_rekening(self):
return self.rekening.rekeningnm
def get_masa_1(self):
return self.invoice.masadari.strftime('%Y%m%d')
def get_masa_2(self):
return self.invoice.masasd.strftime('%Y%m%d')
class Inquiry(BaseInquiry):
def __init__(
self, invoice_id, rekening_notes=[], persen_denda=2,
tgl_bayar=None):
BaseInquiry.__init__(self, invoice_id, rekening_notes)
if not self.invoice:
return
self.tgl_bayar = tgl_bayar or datetime.now()
self.set_profile()
self.persen_denda = persen_denda
self.hitung()
def get_payment_amount(self):
q = DBSession.query(func.sum(Payment.jml_bayar).label('jml'))
q = q.filter_by(spt_id=self.invoice.id)
pay = q.first()
return pay.jml or 0
def hitung(self):
self.bunga = self.invoice.bunga or 0
self.bunga = round_up(self.bunga)
self.tagihan = self.invoice.pajak_terhutang - self.bunga
self.tagihan = round_up(self.tagihan)
self.total_bayar = self.get_payment_amount()
self.total_bayar = round_up(self.total_bayar)
self.total = self.tagihan - self.total_bayar
if self.total <= 0:
self.tagihan = self.total = self.denda = 0
return
self.denda = self.bunga
if self.invoice.jatuhtempotgl:
bulan, self.denda_waktu = hitung_denda(
self.tagihan, self.invoice.jatuhtempotgl, self.persen_denda,
self.tgl_bayar.date())
self.denda += self.denda_waktu
self.denda = round_up(self.denda)
self.total += self.denda
def get_pay_seq(self):
q = DBSession.query(Payment).filter_by(tahun=self.invoice.tahun)
q = q.order_by(Payment.sspdno.desc())
pay = q.first()
if pay:
return pay.sspdno + 1
return 1
def do_payment(self, ntb):
sspdno = self.get_pay_seq()
pay = Payment()
pay.tahun = self.invoice.tahun
pay.spt_id = self.invoice.id
pay.sspdno = sspdno
pay.denda = pay.bunga = self.denda
pay.jml_bayar = self.total
pay.create_date = pay.write_date = datetime.now()
pay.sspdtgl = self.tgl_bayar
pay.printed = pay.enabled = 1
self.invoice.status_pembayaran = 1
DBSession.add(pay)
DBSession.add(self.invoice)
DBSession.flush()
return pay
class Reversal(BaseInquiry):
def __init__(self, invoice_id):
BaseInquiry.__init__(self, invoice_id)
if not self.invoice:
return
self.set_profile()
self.payment = self.get_payment()
def do_reversal(self):
if self.payment:
self.payment.jml_bayar = self.payment.denda = \
self.payment.bunga = 0
self.payment.enabled = 0
DBSession.add(self.payment)
self.invoice.status_pembayaran = 0
DBSession.add(self.invoice)
DBSession.flush()
def get_payment(self):
q = DBSession.query(Payment).filter_by(spt_id=self.invoice.id)
q = q.order_by(Payment.id.desc())
return q.first()
class AvailableInvoice:
def __init__(self, persen_denda=2, option=None):
self.option = option
self.persen_denda = persen_denda
def show(self):
offset = -1
count = 0
max_count_length = len(str(self.option.count))
q = self.get_query()
awal = time()
while True:
if time() - awal > 10:
break
offset += 1
row = q.offset(offset).first()
if not row:
break
msg = self.get_message(row)
if not msg:
continue
count += 1
no = str(count).zfill(max_count_length)
msg = '#{}/{} {}'.format(no, self.option.count, msg)
print(msg)
if count == self.option.count:
break
def get_query(self):
q = DBSession.query(
Invoice.sptno, Invoice.tahun, Rekening.rekeningkd,
Rekening.rekeningnm)
q = q.filter(
Invoice.pajak_id==Pajak.id, Pajak.rekening_id==Rekening.id,
Invoice.status_pembayaran==0)
if self.option.jenis:
pola = '%{}%'.format(self.jenis)
q = q.filter(Rekening.rekeningnm.ilike(pola))
return q
def get_message(self, row):
invoice_id = FixLength(INVOICE_ID)
invoice_id['Tahun'] = row.tahun
invoice_id['SptNo'] = row.sptno
inq = Inquiry(invoice_id.get_raw(), persen_denda=self.persen_denda)
if not inq.total:
return
return '{} {}, {}, Rp {}'.format(
invoice_id.get_raw(), row.rekeningkd, row.rekeningnm,
thousand(inq.total))
import os
import setuptools
with open('README.rst') as f:
long_description = f.read()
with open('CHANGES.txt') as f:
CHANGES = f.read()
line = CHANGES.splitlines()[0]
version = line.split()[0]
requires = [
'sqlalchemy',
'opensipkd-hitung @ git+https://git.opensipkd.com/sugiana/opensipkd-hitung',
]
setuptools.setup(
name='opensipkd-pad-models',
version=version,
author='Owo Sugiana',
author_email='sugiana@gmail.com',
description='Struktur tabel PAD',
long_description=long_description,
long_description_content_type='text/markdown',
license='PostgreSQL License',
install_requires=requires,
packages=setuptools.find_packages(),
classifiers=[
'Programming Language :: Python :: 3',
'Operating System :: OS Independent',
],
entry_points={
'console_scripts': [
'pad_available_invoice = opensipkd.pad.scripts.available_invoice:main',
'pad_inquiry = opensipkd.pad.scripts.inquiry:main',
]
},
)
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!