Di banyak organisasi, evaluasi unit dilakukan dengan menggunakan KPI (Indikator Kinerja Utama). Dalam organisasi tempat saya bekerja, sistem seperti itu disebut "sistem indikator kinerja", dan dalam artikel ini saya ingin berbicara tentang bagaimana kami berhasil mengotomatiskan bagian dari pekerjaan dengan indikator dalam sebulan. Dengan semua ini, biaya tenaga kerja kami bukan yang terbesar, tetapi pada saat yang sama kami mencoba menerapkan beberapa Wishlist lama. Dalam cerita saya tidak akan ada teknologi hype atau wahyu (setelah semua, pembangunan provinsi itu keras), tetapi akan ada beberapa sketsa pada topik yang akan membantu untuk memahami bagaimana kami memulai, apa yang kami lakukan, dan apa yang kami dapatkan dari pemikiran dari pengembangan. Jika Anda belum bosan, saya minta kucing.
Latar belakang
Kembali pada tahun 2006, kami memperkenalkan sistem indikator kinerja: lima belas kriteria evaluasi dikembangkan, metodologi untuk menghitungnya, dan frekuensi triwulanan untuk menghitung indikator ini didirikan. Penilaian dilakukan untuk dua puluh dua cabang organisasi yang berlokasi di semua wilayah di wilayah kami. Dan agar indikator dapat dicapai dengan antusiasme yang besar, bonus dikaitkan dengan mereka - semakin tinggi jumlah indikator, semakin tinggi tempat di peringkat, semakin tinggi tempat di peringkat, semakin tinggi premi, dan seterusnya setiap triwulan dan setiap tahun.
Seiring waktu, komposisi kriteria berubah: setiap kuartal, yang baru ditambahkan atau yang lama dikeluarkan. Pada puncaknya, pada sekitar 2016, jumlah indikator melebihi empat puluh, dan sekarang hanya ada empat belas.
Namun, selama ini proses perhitungannya sama. Setiap kriteria dihitung oleh unit yang bertanggung jawab dari organisasi induk sesuai dengan algoritma yang disetujui secara khusus untuk kriteria ini. Perhitungan dapat dilakukan baik dengan rumus sederhana maupun dengan sejumlah rumit, mungkin memerlukan pengumpulan data dari beberapa sistem, sementara itu sangat mungkin bahwa algoritma akan berubah cukup sering. Dan kemudian semuanya sudah jauh lebih sederhana: indikator persentase yang dihitung dikalikan dengan koefisiennya, dengan demikian, suatu poin diperoleh dengan kriteria, maka poin-poin tersebut diberi peringkat dan masing-masing unit berlangsung sesuai dengan poin tersebut. Hal yang sama dilakukan dengan jumlah poin untuk menghitung tempat terakhir.
Sejak awal, Excel digunakan untuk memperhitungkan dan menghitung semua yang dijelaskan di atas. Selama bertahun-tahun, serangkaian kriteria terakhir dan perhitungan lebih lanjut dari titik dan tempat dilakukan dalam piring yang bagus, yang sebagian ditunjukkan dalam gambar.

Saya hanya mencatat bahwa di tablet yang bagus ini, sebagian besar pengeras suara disembunyikan, tetapi kenyataannya pada kuartal ke-4 tahun 2016 terlihat seperti ini

Setuju, sejumlah indikator dan volume tabel seperti itu tidak menambah kesederhanaannya. Selain itu, masing-masing kriteria dihitung secara terpisah, dan anak perempuan dari departemen umum membuat tabel ringkasan ini dengan tangan mereka. Mempertimbangkan bahwa tidak hanya komposisi kriteria yang berubah, tetapi juga perhitungannya dapat diulang beberapa kali (karena berbagai alasan), hari-hari terakhir setiap kuartal, dengan kata lain, tidak bahagia. Saya harus terus mengubah data dalam tabel, periksa dan periksa ulang: seperti ini, Anda lupa menambahkan kolom baru ke jumlah poin atau Anda tidak memperbarui koefisien, dan untuk beberapa orang tempat semakin rendah, dan dengan itu bonusnya kurang. Oleh karena itu, setelah pengurangan heroik dari tabel, verifikasi heroik dimulai, semua orang yang mempertimbangkan setiap kriteria, manajer, dan bahkan seorang spesialis IT yang ditugaskan secara organisasi memeriksa dan menyetujui. Mengingat bahwa semua pekerjaan dilakukan secara manual, dan perhitungan indikator untuk tabel pivot dikirim ke surat, cukup sering ada situasi ketika tabel pivot berisi kesalahan dalam rumus dan data versi yang tidak relevan. Kapten bukti melaporkan bahwa setelah setiap kesalahan terdeteksi, proses verifikasi dimulai kembali.
Ketika semua indikator dihitung, tablet disetujui dan dikirim ke cabang, di sana, di cabang itu diperiksa ulang: bagaimana jika Excel yang berbahaya berpikir ada sesuatu yang salah? Saya pikir jelas bahwa menyusun laporan tahunan tentang kriteria atau menganalisis data historis ternyata menjadi pencarian yang tidak kalah menarik.
Namun, dari sudut pandang manajerial, sistem seperti itu cukup efektif dan memungkinkan, jika perlu, untuk memperketat area kerja yang kendur, memantau dan memiliki gagasan tentang apa yang terjadi di masing-masing cabang di area kerja tertentu. Selain itu, lokasi cabang, dihitung sesuai dengan kriteria, adalah indikator yang tidak terpisahkan.
Sistem harus berubah
Praktek telah membuktikan perlunya dan pentingnya sistem penilaian. Pada awal 2017, menjadi jelas bahwa menghitung kriteria sekali seperempat memungkinkan untuk mengevaluasi pekerjaan yang dilakukan, tetapi pemantauan itu membuatnya menjadi lemah. Rentang waktu terlalu lama. Dalam hal ini, diputuskan bahwa perhitungan indikator akan dilakukan setiap dua minggu sekali, dan hasilnya akan diringkas semua setiap triwulan yang sama. Peningkatan frekuensi perhitungan benar-benar memungkinkan manajer cabang untuk dengan cepat merespons perubahan dan meningkatkan kontrol atas proses dalam unit mereka.
Tetapi hanya di organisasi induk, sedikit yang senang dengan prospek melakukan proses pengumpulan data yang dijelaskan di atas, tidak setiap kuartal, tetapi setiap dua minggu. Karena tidak sulit ditebak, mereka memutuskan untuk mengotomatiskan prosesnya. Tenggat waktu ternyata ketat: dari saat keputusan untuk beralih ke frekuensi perhitungan baru, ke transisi yang sebenarnya, hanya sebulan yang seharusnya berlalu. Untuk periode ini, sangat diinginkan untuk menghasilkan sesuatu yang lebih baik daripada sekelompok Excel untuk kode dan perhitungan, dan surat untuk pengumpulan dan pemberitahuan.
Pada awalnya ada sedikit diskusi panas tentang apa yang benar-benar perlu dilakukan secara otomatis dan perhitungan indikator itu sendiri, dengan mempertimbangkan semua formula dan sumber data mereka. Tetapi mengingat tenggat waktu yang ketat, kompleksitas fungsi tersebut dan perlunya pemeliharaan konstan dalam keadaan saat ini berhasil memenuhi persyaratan berikut untuk sistem:
- Manual referensi kriteria harus disimpan, menjaga sejarah perubahan mereka;
- Seharusnya dimungkinkan untuk memasukkan dan menyimpan indikator yang dihitung, serta konversi mereka ke poin seperti pada lempeng yang sama;
- Berdasarkan nilai-nilai indikator, laporan harus dihasilkan yang dapat diakses oleh semua pihak yang berkepentingan;
- Secara alami, semua ini harus disediakan dengan antarmuka web.
Fungsionalitasnya sangat kecil, tetapi tidak banyak waktu.
Pengembangan dimulai
Saya selalu tertarik dengan cara pengembangan yang cepat dan generasi antarmuka otomatis. Dalam kasus di mana perlu untuk mengimplementasikan fungsionalitas CRUD, gagasan ketika antarmuka dan bagian dari logika bisnis akan disediakan di luar kotak, sehingga untuk berbicara, tampaknya sangat menggoda. Tentu saja, antarmuka akan menjadi logika bersahaja dan canggung, tetapi untuk banyak tugas ini sudah cukup.
Didorong oleh ide-ide ini, saya memutuskan untuk mencoba sesuatu seperti itu. Ada Spring Roo, Kuba dan alat menarik lainnya, tetapi pilihannya jatuh pada OpenXava. Pertama, sekali saya sudah membuat aplikasi yang sangat sederhana dan puas, dan kedua, kerangka kerja ini pada waktu itu cukup berhasil masuk ke tumpukan teknologi kami. Selain itu, sangat menyenangkan untuk memiliki tutorial singkat dalam bahasa Rusia.
Deskripsi singkat tentang fitur dan kemampuan OpenXava dapat ditemukan di
sini . OpenXava adalah kerangka kerja yang mengimplementasikan konstruksi otomatis antarmuka web yang terintegrasi dengan basis data berdasarkan JPA, dan menggunakan anotasi untuk menggambarkan aturan visualisasi. Aplikasi ini didasarkan pada komponen bisnis - kelas Java yang berisi informasi yang diperlukan untuk membuat aplikasi. Informasi tersebut termasuk struktur data, validator, representasi valid, pemetaan ke tabel database. Operasi pada komponen bisnis dilakukan melalui pengontrol yang dapat CRUD dari kotak, pencarian, ekspor ke PDF, dll. Aplikasi OpenXava menyajikan satu set modul. Modul mengaitkan komponen bisnis dengan satu atau lebih pengontrol. Representasi yang ditentukan untuk setiap komponen bisnis digunakan untuk menampilkan antarmuka. Tidak ada yang aneh, MVC dengan sedikit atmosfernya sendiri.
Penyimpanan data
Di sebagian besar aplikasi, kami menggunakan DBMS IBM DB2. Database kecil dibuat yang menyimpan buku referensi kelompok kriteria dan kriteria di mana cabang dievaluasi, direktori cabang dan piring di mana nilai-nilai terhitung dari kriteria dimasukkan. Untuk setiap kriteria, pada titik waktu tertentu, sebuah koefisien ditetapkan yang akan digunakan dalam penilaian. Setiap cabang, untuk setiap kriteria, menerima penilaian juga untuk tanggal tertentu. Data tentang nilai-nilai kriteria disimpan secara historis, yaitu data yang relevan dengan tanggal apa pun adalah data yang dimasukkan pada tanggal terdekat di masa lalu. Pendekatan ini, terinspirasi oleh register informasi dari 1C: Enterprise, tampaknya cukup nyaman bagi saya: ada sejarah dan masalah pengeditan / penghapusan tidak terlalu melonjak.
Struktur basis dataCREATE TABLE SUMMAR.CLS_DEPART ( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, PARENT_ID BIGINT NOT NULL DEFAULT 0, IS_DELETED INT DEFAULT 0, NAME CLOB, CODE VARCHAR(255), PRIMARY KEY (ID) ); CREATE TABLE SUMMAR.CLS_CRITERIA ( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, IS_DELETED INT DEFAULT 0, NAME CLOB, CODE VARCHAR(255), PRIMARY KEY (ID) ); CREATE TABLE SUMMAR.CLS_GROUP_CRITERIA ( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, IS_DELETED INT DEFAULT 0, NAME CLOB, CODE VARCHAR(255), PRIMARY KEY (ID) ); CREATE TABLE SUMMAR.REG_STATE_CRITERIA ( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, ID_CRITERIA BIGINT NOT NULL, ID_GROUP_CRITERIA BIGINT NOT NULL, TIME_BEGIN TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP, TIME_END TIMESTAMP NOT NULL DEFAULT '9999-12-31-23.59.59.000000000000', TIME_CREATE TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP, KOEFFICIENT DECIMAL(15, 2), PRIMARY KEY (ID), CONSTRAINT FK_CRITERIA FOREIGN KEY (ID_CRITERIA) REFERENCES SUMMAR.CLS_CRITERIA(ID) ON DELETE NO ACTION ON UPDATE RESTRICT, CONSTRAINT FK_GROUP_CRITERIA FOREIGN KEY (ID_GROUP_CRITERIA) REFERENCES SUMMAR.CLS_GROUP_CRITERIA(ID) ON DELETE NO ACTION ON UPDATE RESTRICT ); CREATE TABLE SUMMAR.REG_VALUE_CRITERIA ( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, ID_CRITERIA BIGINT NOT NULL, ID_GROUP_CRITERIA BIGINT NOT NULL, ID_DEPART BIGINT NOT NULL, DATE_REG TIMESTAMP(12) NOT NULL DEFAULT CURRENT TIMESTAMP, TIME_END TIMESTAMP NOT NULL DEFAULT '9999-12-31-23.59.59.000000000000', TIME_BEGIN TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP, PERCENT DECIMAL(15, 5), VAL DECIMAL(15, 5), PRIMARY KEY (ID), CONSTRAINT FK_CRITERIA FOREIGN KEY (ID_CRITERIA) REFERENCES SUMMAR.CLS_CRITERIA(ID) ON DELETE NO ACTION ON UPDATE RESTRICT, CONSTRAINT FK_DEPART FOREIGN KEY (ID_DEPART) REFERENCES SUMMAR.CLS_DEPART(ID) ON DELETE NO ACTION ON UPDATE RESTRICT, CONSTRAINT FK_GROUP_CRITERIA FOREIGN KEY (ID_GROUP_CRITERIA) REFERENCES SUMMAR.CLS_GROUP_CRITERIA(ID) ON DELETE NO ACTION ON UPDATE RESTRICT );
Untuk mendapatkan laporan yang didambakan tentang kinerja cabang dari database, fungsi yang tersimpan dibuat, yang hasilnya sudah dipetakan ke kelas Java. Fungsinya terlihat seperti ini.
Pertama, kami mendapatkan semua kriteria yang valid untuk tanggal tersebut, serta koefisien kriteria yang relevan untuk tanggal tersebut CREATE OR REPLACE FUNCTION SUMMAR.SLICE_STATE_ALL_CRITERIA ( PMAX_TIME TIMESTAMP ) RETURNS TABLE ( ID_CRITERIA BIGINT, ID_GROUP_CRITERIA BIGINT, TIME_BEGIN TIMESTAMP, TIME_END TIMESTAMP, TIME_CREATE TIMESTAMP, KOEFFICIENT DECIMAL(15, 2) ) LANGUAGE SQL RETURN SELECT RSC.ID_CRITERIA, RSC.ID_GROUP_CRITERIA, RSC.TIME_BEGIN, RSC.TIME_END, RSC.TIME_CREATE, RSC.KOEFFICIENT FROM SUMMAR.REG_STATE_CRITERIA AS RSC INNER JOIN ( SELECT ID_CRITERIA, MAX(TIME_BEGIN) AS TIME_BEGIN FROM ( SELECT DISTINCT ID_CRITERIA, TIME_BEGIN FROM SUMMAR.REG_STATE_CRITERIA WHERE TIME_BEGIN < PMAX_TIME AND TIME_END > PMAX_TIME ) AS SL GROUP BY ID_CRITERIA ) AS MAX_SLICE ON RSC.ID_CRITERIA = MAX_SLICE.ID_CRITERIA AND RSC.TIME_BEGIN = MAX_SLICE.TIME_BEGIN ;
Sekarang kami akan menerima pada tanggal yang sama nilai dari semua kriteria untuk semua cabang CREATE OR REPLACE FUNCTION SUMMAR.SLICE_VALUE_ACTUAL_ALL_CRITERIA_ALL_DEPART_WITH_NAMES ( PMAX_TIME TIMESTAMP ) RETURNS TABLE ( ID_CRITERIA BIGINT, ID_GROUP_CRITERIA BIGINT, ID_DEPART BIGINT, DATE_REG TIMESTAMP, PERCENT DECIMAL(15, 2), VAL DECIMAL(15, 2), KOEFFICIENT DECIMAL(15, 2), CRITERIA_NAME CLOB, CRITERIA_CODE VARCHAR(255), GROUP_CRITERIA_NAME CLOB, GROUP_CRITERIA_CODE VARCHAR(255), DEPART_NAME CLOB, DEPART_CODE VARCHAR(255), DEPART_CODE_INT INT ) LANGUAGE SQL RETURN SELECT CDEP.ID_CRITERIA, COALESCE(VALS.ID_GROUP_CRITERIA, 0) AS ID_GROUP_CRITERIA, CDEP.ID_DEPART, VALS.DATE_REG, COALESCE(VALS.PERCENT, 0.0) AS PERCENT, COALESCE(VALS.VAL, 0.0) AS VAL, COALESCE(VALS.KOEFFICIENT, 0.0) AS KOEFFICIENT, CDEP.CRITERIA_NAME, CDEP.CRITERIA_CODE, COALESCE(VALS.GROUP_CRITERIA_NAME, '') AS GROUP_CRITERIA_NAME, COALESCE(VALS.GROUP_CRITERIA_CODE, '') AS GROUP_CRITERIA_CODE, CDEP.DEPART_NAME, CDEP.DEPART_CODE, CDEP.DEPART_CODE_INT FROM ( SELECT CCRT.ID AS ID_CRITERIA, CCRT."NAME" AS CRITERIA_NAME, CCRT.CODE AS CRITERIA_CODE, CDEP.ID AS ID_DEPART, CDEP."NAME" AS DEPART_NAME, CDEP.CODE AS DEPART_CODE, CAST (CDEP.CODE AS INT) AS DEPART_CODE_INT FROM SUMMAR.CLS_DEPART AS CDEP, ( SELECT * FROM SUMMAR.CLS_CRITERIA AS CC INNER JOIN TABLE(SUMMAR.SLICE_STATE_ALL_CRITERIA (PMAX_TIME)) AS ACTC ON CC.ID = ACTC.ID_CRITERIA WHERE CC.IS_DELETED = 0 ) AS CCRT WHERE CDEP.IS_DELETED = 0 ) AS CDEP LEFT JOIN ( SELECT VALS.ID_CRITERIA, VALS.ID_GROUP_CRITERIA, VALS.ID_DEPART, VALS.DATE_REG, VALS.PERCENT, VALS.VAL, VALS.KOEFFICIENT, CGRT."NAME" AS GROUP_CRITERIA_NAME, CGRT.CODE AS GROUP_CRITERIA_CODE FROM TABLE(SUMMAR.SLICE_VALUE_ACTUAL_ALL_CRITERIA (PMAX_TIME)) AS VALS INNER JOIN SUMMAR.CLS_GROUP_CRITERIA AS CGRT ON VALS.ID_GROUP_CRITERIA = CGRT.ID ) as VALS ON CDEP.ID_DEPART = VALS.ID_DEPART AND CDEP.ID_CRITERIA = VALS.ID_CRITERIA ;
Dalam kueri akhir, kami menomori nilai indikator, memberi peringkat, dan menemukan minimum dan maksimum, ini akan diperlukan untuk menghitung tempat nanti SELECT ROW_NUMBER() OVER() AS ID_NUM, RANK() OVER( PARTITION BY ID_CRITERIA ORDER BY VAL DESC ) AS RATING, CASE WHEN MAX(RANK() OVER( PARTITION BY ID_CRITERIA ORDER BY VAL DESC ) ) OVER() = RANK() OVER( PARTITION BY ID_CRITERIA ORDER BY VAL DESC ) THEN 1 ELSE 0 END AS MAX_RATING, CASE WHEN MIN(RANK() OVER( PARTITION BY ID_CRITERIA ORDER BY VAL DESC ) ) OVER() = RANK() OVER( PARTITION BY ID_CRITERIA ORDER BY VAL DESC ) THEN 1 ELSE 0 END AS MIN_RATING, VALS.* FROM TABLE(SUMMAR.SLICE_VALUE_ACTUAL_ALL_CRITERIA_ALL_DEPART_WITH_NAMES (?)) AS VALS ORDER BY GROUP_CRITERIA_CODE, CRITERIA_CODE, DEPART_CODE_INT
Transfer bagian dari logika bisnis ke DBMS cukup dibenarkan, terutama ketika harus menyiapkan data untuk laporan yang berbeda. Ternyata operasi dapat ditulis dalam bentuk yang lebih ringkas dan alami, di Jawa, manipulasi dengan data akan memerlukan jumlah kode yang lebih besar dan beberapa upaya untuk menyusunnya. Meskipun operasi yang relatif kompleks atau nontrivial masih lebih mudah untuk diprogram di Jawa. Oleh karena itu, dalam aplikasi kami, berkenaan dengan pengambilan sampel data, suatu pendekatan digunakan di mana koneksi set data, kondisi kliping dan beberapa operasi yang dapat dilakukan oleh fungsi jendela dilakukan dalam fungsi dan prosedur yang tersimpan, dan logika yang lebih kompleks diterapkan dalam aplikasi.
Aplikasi
Seperti yang saya katakan, OpenXava digunakan untuk mengimplementasikan aplikasi. Untuk mendapatkan antarmuka khas dan CRUD di luar kotak, Anda perlu melakukan beberapa tindakan.
Untuk memulainya, di web.xml Anda perlu menjelaskan filter dan servlet dari addon yang menavigasi aplikasi:
web.xml <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <filter> <filter-name>naviox</filter-name> <filter-class>com.openxava.naviox.web.NaviOXFilter</filter-class> </filter> <filter-mapping> <filter-name>naviox</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> <filter-mapping> <filter-name>naviox</filter-name> <url-pattern>/modules/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> <filter-mapping> <filter-name>naviox</filter-name> <servlet-name>naviox</servlet-name> </filter-mapping> <filter-mapping> <filter-name>naviox</filter-name> <servlet-name>module</servlet-name> </filter-mapping> <servlet> <servlet-name>naviox</servlet-name> <servlet-class>com.openxava.naviox.web.NaviOXServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>naviox</servlet-name> <url-pattern>/m/*</url-pattern> </servlet-mapping> </web-app>
Selanjutnya, dalam file controllers.xml, kami mendefinisikan controller yang digunakan dalam aplikasi. Dalam kasus kami, yang paling sederhana sudah cukup:
controllers.xml <controllers> <controller name="Typical_View"> <extends controller="Navigation"/> <extends controller="CRUD"/> <extends controller="ExtendedPrint"/> </controller> </controllers>
Kontroler di atas menggabungkan fungsi-fungsi dari kontroler yang termasuk dalam OpenXava secara default, fungsi-fungsi yang mudah ditebak dari namanya.
Dan akhirnya, dalam file application.xml kita akan menghubungkan controller yang dibuat dan modelnya. Sesuatu seperti ini:
application.xml <application name="summar"> <module name="RegValueCriteria"> <model name="RegValueCriteria"/> <controller name="Typical_View"/> </module> </application>
Seperti disebutkan di atas, aplikasi didasarkan pada komponen bisnis yang membentuk model aplikasi. Misalnya, pertimbangkan komponen RegValueCriteria yang terkait dengan pengontrol di application.xml. Komponen ini menjelaskan nilai kriteria untuk cabang (untuk singkatnya, hanya uraian bidang kelas yang tersisa, dan saya akan menghilangkan metode seperti getter dan setter):
Kelas komponen @Entity @Table(name = "REG_VALUE_CRITERIA", catalog = "", schema = "SUMMAR") @XmlRootElement @Views({ @View(members = "idCriteria [idCriteria];" + "idGroupCriteria [idGroupCriteria];" + "idDepart [idDepart];" + "data [dateReg, percent, val]"), @View(name="massReg", members = "idDepart.name, percent, val") }) @Tab(properties= "idDepart.name, idCriteria.name, idGroupCriteria.name, dateReg, percent, val" ) public class RegValueCriteria implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "ID") private Long id; @Basic(optional = false) @NotNull @Column(name = "DATE_REG") @Temporal(TemporalType.TIMESTAMP) @DefaultValueCalculator(CurrentDateCalculator.class) @Stereotype("DATETIME") private Date dateReg; @Column(name = "PERCENT") @OnChange(OnChangePercentAction.class) private BigDecimal percent; @Column(name = "VAL") private BigDecimal val; @JoinColumn(name = "ID_CRITERIA", referencedColumnName = "ID") @ManyToOne(optional = false) @DescriptionsList( descriptionProperties="name" ) @OnChange(OnChangeClsCriteriaAction.class) private ClsCriteria idCriteria; @JoinColumn(name = "ID_GROUP_CRITERIA", referencedColumnName = "ID") @ManyToOne(optional = false) @DescriptionsList( descriptionProperties="name" ) private ClsGroupCriteria idGroupCriteria; @JoinColumn(name = "ID_DEPART", referencedColumnName = "ID") @ManyToOne(optional = false) @DescriptionsList( descriptionProperties="name" ) private ClsDepart idDepart; }
Selain anotasi JPA biasa. Anda juga dapat melihat anotasi OpenXava. Mereka harus dipertimbangkan secara lebih rinci.
@View
memungkinkan
@View
untuk mengontrol format presentasi bidang kelas, dan dengan bantuan sintaks khusus dalam bentuk tanda kurung, bidang dapat dikelompokkan dan diatur secara horizontal dan vertikal menggunakan simbol
,
dan
;
. Jika beberapa pemetaan perlu ditentukan untuk satu komponen, maka penjelasan
@View
dikelompokkan menggunakan penjelasan
@View
. Dalam contoh kami, properti diatur sebagai berikut:
@View(members = "idCriteria [idCriteria];" + "idGroupCriteria [idGroupCriteria];" + "idDepart [idDepart];" + "data [dateReg, percent, val]")
Dan terlihat seperti ini:

Bersahaja, tetapi dengan upaya minimal. Namun, ada beberapa cara untuk "menghidupkan kembali" formulir itu sedikit.
Agar tanggal registrasi diisi ketika membuat komponen, penjelasan
@DefaultValueCalculator
, yang memanggil objek kalkulator khusus. Di sini, kalkulator dari OpenXava sendiri digunakan, tetapi Anda juga bisa membuatnya sendiri. Anotasi
@Stereotype
digunakan untuk menampilkan tanggal menggunakan kontrol yang sesuai.
Untuk mengonfigurasi daftar turun bawah yang berisi objek terkait, penjelasan
@DescriptionsList
, di mana Anda dapat menentukan properti mana yang akan ditampilkan dalam daftar.
Menggunakan anotasi, Anda bisa menerapkan beberapa logika bisnis dari formulir itu sendiri. Misalnya, sehingga ketika persentase berubah, nilai dihitung secara otomatis dengan mempertimbangkan koefisien kriteria, Anda dapat menerapkan anotasi
@OnChange
untuk bidang
BigDecimal val
. Agar penjelasan
@OnChange
berfungsi, perlu menunjuk ke kelas yang mengimplementasikan antarmuka
OnChangePropertyBaseAction
. Metode single
execute()
harus diimplementasikan di kelas, di mana input data diambil dari tampilan, perhitungan dilakukan dan nilai yang dihitung ditulis kembali ke tampilan:
Kelas anak OnChangePropertyBaseAction public class OnChangePercentAction extends OnChangePropertyBaseAction{ @Override public void execute() throws Exception { BigDecimal percent = (BigDecimal)getNewValue(); if (percent != null){ Map value = (Map)getView().getValue("idCriteria"); if (value != null){ Long idCriteria = (Long)value.get("id"); Query query = XPersistence.getManager().createNativeQuery( "SELECT KOEFFICIENT FROM SUMMAR.SLCLA_STATE_CRITERIA WHERE ID_CRITERIA = ?"); query.setParameter(1, idCriteria); List<?> list = query.getResultList(); if (list != null && !list.isEmpty()){ BigDecimal koef = (BigDecimal) list.get(0); BigDecimal vl = koef.multiply(percent); getView().setValue("val", vl); } } } } }
Untuk presentasi data tabular, anotasi
@Tab
, yang memungkinkan Anda untuk membuat daftar properti objek yang akan ditampilkan dalam representasi tabular. Dalam contoh kami, anotasi diatur sebagai berikut:
@Tab(properties= "idDepart.name, idCriteria.name, idGroupCriteria.name, dateReg, percent, val" )
Ini akan terlihat sebagai berikut

Senang dengan ketersediaan filter, pagination dan ekspor di luar kotak, namun, banyak detail memerlukan penyempurnaan dengan file.
Pekerjaan dengan komponen lain juga dibangun dengan cara yang sama. Menggunakan OpenXava telah secara dramatis mengurangi biaya tenaga kerja untuk mengimplementasikan fungsi CRUD dan sebagian besar antarmuka pengguna. Menggunakan tindakan dari pengontrol yang telah ditentukan dan anotasi untuk membangun formulir sangat menghemat waktu jika Anda tidak menemukan kesalahan dengan detail dan mencoba menerapkan sesuatu yang lebih rumit daripada formulir input dengan beberapa peristiwa. Meskipun mungkin demikian pengalamannya.
Tentang apa itu semua
Ingat untuk apa aplikasi itu? Ya, sehingga pelat dengan indikator tidak direduksi secara heroik di Excel, tetapi dibuat secara otomatis berdasarkan data yang dimasukkan. Di jendela browser, tabel pivot mulai terlihat seperti ini:


Saya tidak akan memberikan detail implementasi, karena semuanya tidak terlalu baik dengan itu dan campuran JSP dengan HTML yang dihasilkan ketika meminta data bukan sesuatu untuk dibagikan kepada masyarakat umum. Pada saat yang sama, pengambilan sampel data itu sendiri ditunjukkan di atas.
Namun, saya ingin membahas satu detail menarik. Ketika persyaratan untuk aplikasi dikumpulkan, manajemen benar-benar menginginkan bahwa, di samping laporan ringkasan, nilai-nilai indikator individual dapat ditampilkan dalam bentuk peta wilayah, dibagi menjadi wilayah-wilayah dengan indikasi lokasi dan skor cabang terkait. Siapa yang mengenali wilayah di peta - dilakukan dengan baik =)

Di satu sisi, persyaratannya adalah opsional, tetapi di sisi lain, gambar berjanji untuk visual, dan dari sudut pandang implementasi itu menarik untuk dicoba. Setelah beberapa pemikiran, muncul ide untuk menemukan gambar wilayah dalam format SVG dan membuat template XSLT darinya.
Template yang dihasilkan mudah diisi dengan data, dan setelah itu dikonversi ke PNG.
Pertama, menggunakan kueri yang dijelaskan di atas, data diambil sampelnya, data yang diterima dikonversi menjadi objek kelas ini:
Kelas untuk menghasilkan data ke peta @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "VisualisedValuesCriteria") public class VisualisedValuesCriteria { private XMLGregorianCalendar date; private String criteriaName; private String koefficient; private List<VisualizedValueCriteria> departValues; public XMLGregorianCalendar getDate() { return date; } public void setDate(XMLGregorianCalendar date) { this.date = date; } public String getCriteriaName() { return criteriaName; } public void setCriteriaName(String criteriaName) { this.criteriaName = criteriaName; } public String getKoefficient() { return koefficient; } public void setKoefficient(String koefficient) { this.koefficient = koefficient; } public List<VisualizedValueCriteria> getDepartValues() { if (departValues == null){ departValues = new ArrayList<>(); } return departValues; } public void setDepartValues(List<VisualizedValueCriteria> departValues) { this.departValues = departValues; } } @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class VisualizedValueCriteria { private String departName; private String departCode; private String value; private String percent; private String colorCode; public String getDepartName() { return departName; } public void setDepartName(String departName) { this.departName = departName; } public String getDepartCode() { return departCode; } public void setDepartCode(String departCode) { this.departCode = departCode; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String getPercent() { return percent; } public void setPercent(String percent) { this.percent = percent; } public String getColorCode() { return colorCode; } public void setColorCode(String colorCode) { this.colorCode = colorCode; } } }
Selanjutnya, konversikan objek ke XML;
Konversi Private String marshal (VisualisedValuesCriteria obj){ final Marshaller marshaller = JAXBContext.newInstance(VisualisedValuesCriteria.class).createMarshaller(); marshaller.setEventHandler(new DefaultValidationEventHandler()); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); StringWriter writer = new StringWriter(); marshaller.marshal(obj, writer); return writer.toString(); }
Sekarang, mari kita ambil XML yang dihasilkan, template XSLT yang telah disiapkan, terapkan konversi dan dapatkan svg di output:
Dapatkan svg String xml = marshal(obj); TransformerFactory factory = TransformerFactory.newInstance(); FileInputStream xsltFis = new FileInputStream("C:\\TEMP\\map_xsl.svg"); InputStreamReader xsltIn = new InputStreamReader(xsltFis, "UTF-8"); Source xslt = new StreamSource(xsltIn); Transformer transformer = factory.newTransformer(xslt); InputStream xmlIn = new ByteArrayInputStream( xml.getBytes( "UTF-8" ) ); Source text = new StreamSource(xmlIn); String filename = "map" + System.currentTimeMillis() + ".svg"; String filePath = "C:\\TEMP\\" + filename; transformer.transform(text, new StreamResult(new File(filePath)));
Pada prinsipnya, kita bisa berhenti di sini, browser menampilkan SVG tanpa masalah. Tetapi laporan yang dijelaskan juga diperoleh melalui bot Telegram, jadi SVG harus dikonversi ke beberapa format seperti JPEG atau PNG. Untuk melakukan ini, gunakan
Apache BatikKonversikan ke PNG private String convertToPNG(final String svg, final String filename, final String filePath){ String png = filePath + filename + ".png"; try { PNGTranscoder trancoder = new PNGTranscoder(); String svgURI = new File(svg).toURL().toString(); TranscoderInput input = new TranscoderInput(svgURI); OutputStream ostream = new FileOutputStream(png); TranscoderOutput output = new TranscoderOutput(ostream); trancoder.transcode(input, output); ostream.flush(); ostream.close(); return filename + ".png"; } catch (MalformedURLException ex) { Logger.getLogger(ActualCriteriaValueGraphicServlet.class.getName()).log(Level.SEVERE, null, ex); } catch (FileNotFoundException | TranscoderException ex) { Logger.getLogger(ActualCriteriaValueGraphicServlet.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(ActualCriteriaValueGraphicServlet.class.getName()).log(Level.SEVERE, null, ex); } return null; }
Laporan dalam bentuk peta sudah siap. Itu juga dapat dilihat melalui browser dan diminta dari bot telegram. Menurut saya, tidak buruk.
Kesimpulan
Pada waktu yang ditentukan, kami memiliki waktu dan sudah pada bulan Maret 2017, indikator kinerja bukannya Excel mulai dimasukkan secara teratur ke dalam sistem yang dibuat. Di satu sisi, masalah maksimum belum terpecahkan, bagian yang paling sulit dari penghitungan indikator dilakukan secara manual. Namun di sisi lain, penerapan perhitungan ini membawa risiko peningkatan yang konstan. Selain itu, bahkan antarmuka sederhana yang dibuat untuk mengumpulkan data menghapus sejumlah besar pertanyaan dengan perubahan konstan, kontrol versi, dan ringkasan lembar kerja Excel. Sejumlah besar pekerjaan manual, cek dan cek silang telah dihapus.
Tidak mungkin untuk tidak mengatakan bahwa antarmuka di Open Xav tidak terlalu menyenangkan bagi pengguna. Pada awalnya, ada banyak pertanyaan pada fitur-fiturnya. Di beberapa titik, pengguna mulai mengeluh bahwa butuh terlalu banyak waktu untuk memasukkan data dan, secara umum, "kami ingin, seperti di Excel, hanya sebuah program." Saya bahkan harus memonitor kecepatan input berdasarkan data pada saat pembuatan catatan. Pemantauan ini menunjukkan bahwa bahkan dalam kasus yang paling parah, pengguna tidak menghabiskan lebih dari 15 menit pada input, tetapi biasanya masuk dalam 5-7, meskipun fakta bahwa mereka perlu memasukkan data di 22 cabang. Indikator seperti itu tampaknya cukup dapat diterima.
Namun, saya ingin mencatat dua hal:
- Open Xava terbukti menjadi alat yang baik untuk membuat antarmuka dengan cepat. Saya bahkan akan mengatakan antarmuka prototipe. Juga keuntungan yang tidak diragukan lagi adalah keteraturan dan keteraturan umum. Semua bentuk dalam aplikasi dibuat sesuai dengan prinsip-prinsip yang seragam, yang memungkinkan pengembang untuk tidak datang dengan sepeda di mana ia tidak membutuhkannya, tetapi pengguna untuk berurusan dengan set formulir standar. Namun, upaya untuk menerapkan logika yang lebih kompleks atau mengubah kontrol untuk diri kita sendiri membawa kita ke sejumlah masalah yang tidak dapat kita atasi dalam waktu yang ditentukan. Kemungkinan besar, kami hanya tidak mengerti, dan tujuannya adalah untuk membuat antarmuka CRUD sederhana dengan upaya minimal. Bagi saya sendiri, saya menyimpulkan bahwa Open Xava adalah alat yang menarik di mana mudah untuk melakukan hal-hal sederhana, tetapi jika Anda perlu melakukan sesuatu yang rumit, saya lebih suka menghabiskan lebih banyak waktu membuat bagian klien menggunakan ExtJS atau React, tetapi memiliki lebih banyak fleksibilitas .
- Bahkan pengguna yang percaya diri membuat antarmuka baru menjadi sulit. Ini tentu saja bukan rahasia. Menurut pendapat saya, ini terutama karena kurangnya pemahaman tentang sifat sistemik dari banyak antarmuka. Bagi banyak orang, aplikasi apa pun memiliki seperangkat layar, yang masing-masing unik dan berfungsi sesuai dengan prinsipnya yang tidak diketahui: misalnya, ada formulir dengan daftar objek / garis (daftar formulir), tetapi bagi begitu banyak pengguna, sama sekali tidak jelas bahwa setiap formulir seperti itu dalam aplikasi dapat memiliki penyaringan seragam, pagination, fungsi penyortiran, dan umumnya perilaku yang sama, ditambah dengan fungsi spesifik. Ini diperparah oleh kenyataan bahwa sejumlah besar perangkat lunak perusahaan adalah tumpukan tombol dan formulir yang hampir spontan, dibumbui dengan dokumentasi tidak jelas dalam gaya "Klik tombol ini." Dari sudut pandang ini, antarmuka yang dibuat oleh pengembang pengguna disiplin Open Xav yang sama, membuat lebih banyak urutan di kepala. Benar, dengan pendekatan ini, tombol ajaib tidak menghilang di mana pun, tetapi menjadi tertata rapi dalam bentuk.
Jika kita berbicara tentang manfaat bagi organisasi secara keseluruhan, maka setelah aplikasi diperkenalkan, seperti yang diharapkan, tingkat kontrol meningkat. Pemimpin lapangan menerima alat untuk memantau indikator, yang dengannya mereka dapat mempengaruhi pekerjaan dalam mode yang lebih operasional, membandingkan indikator untuk periode yang berbeda, tanpa bingung dengan aliran file yang dikirim. Kepentingan para pemimpin dalam pengawasan yang konstan dan waspada terhadap cabang mereka ditemukan oleh kami dengan cara yang sangat menarik: sambil melihat-lihat log bot Telegram, kami memperhatikan bahwa beberapa laporan diterima pada jam 4 atau 5 pagi. Anda bisa langsung melihat siapa yang tertarik dengan pekerjaan.
Itu saja. Saya akan berterima kasih atas umpan balik yang membangun!