DentalZaikoMaker

外掛說明

DentalZaikoMaker is an inventory and expense management plugin built for dental clinics and dental laboratories. The plugin tracks lots and expiry dates, requires at least one registered supplier per material so reordering is always possible, highlights the recommended supplier based on price, events, and purchase history, and visualizes monthly savings. The user interface ships in Japanese for the Japanese dental market.

Key features

  • JAN code based inventory registration and management, with automatic sync to the companion material database web service
  • Smartphone barcode scanning via PWA: JAN/EAN barcodes plus GS1/UDI codes on medical-supply packaging (1D GS1-128 / GS1 DataBar, and 2D DataMatrix/QR). GS1 codes auto-fill the lot number and expiry date on stock-in. Decoding is done in the browser by a bundled pure-JavaScript library; codes can always be typed in by hand instead.
  • Lot number and expiry date tracking with expiration alerts
  • Low stock alerts (in-screen and email)
  • Order request and administrator approval workflow
  • Three role tiers: administrator (approves orders, edits budgets), staff (stock in, use and dispose), and view-only
  • Offline-capable Service Worker cache
  • Expense dashboard with monthly trend, supplier, category, and branch axes (Chart.js)
  • Monthly budget management with 80% and 100% threshold alerts
  • Savings visualization with per-material ranking
  • Weekly recommendation engine: overstock, dead stock, joint purchase
  • CSV export in freee and Money Forward formats
  • Recommended supplier highlighting
  • Cross-branch unit price comparison
  • Local price-trend chart (own clinic data, no external transmission)
  • Optional opt-in monthly statistics report for public price-trend reference (OFF by default, anonymous by default)

Camera scanning

Barcode decoding runs entirely in the browser, using the bundled pure-JavaScript library @zxing/library (public/js/vendor/zxing-library.js, upstream’s unminified build). There is no WebAssembly and no binary in this package, and nothing is downloaded from any external host: camera frames never leave the device, and only the resulting code string is sent to the site.

Formats read by the camera: EAN-13 / EAN-8 / UPC-A (JAN), Code 128 (GS1-128), GS1 DataBar (RSS-14), GS1 DataBar Expanded, DataMatrix and QR. GS1 DataBar Limited and UPC-E are not decoded by this library and are typed in by hand. Camera scanning needs a browser with camera access; on any device, codes can be entered manually instead.

External service integration

This plugin connects to the companion material database web service at https://healthbanks.org/dzm/.
Data exchanged includes the corporation ID, JAN/in-store codes, order details, and material feedback posts; see
“Communication timing” below for the full list of what is sent, when, and by which feature.
Integration can be disabled at any time with the “連携を解除する” (revoke) button on the settings screen. Revoking deletes the stored consent and access tokens; from that moment the plugin sends no API request of any kind, and every scheduled task that would contact the service stops. Your local inventory data is kept.

  • Service: https://healthbanks.org/dzm/
  • Terms of Use: https://healthbanks.org/dzm/terms
  • Privacy Policy: https://healthbanks.org/dzm/privacy

In addition, the plugin offers an OPTIONAL monthly statistics report (see “Optional monthly statistics report” below).
This report is OFF by default and requires a separate, explicit opt-in.

Optional monthly statistics report (opt-in only)

To help public bodies (such as dental associations and the Ministry of Health, Labour and Welfare) understand
material price trends — for example as reference material for insurance fee reviews — the plugin can send an
aggregated monthly statistics report to the same web service (https://healthbanks.org/dzm/).

  • This feature is DISABLED by default. Nothing is sent unless the clinic administrator explicitly opts in on the
    “Public report” settings screen and acknowledges what is sent.
  • When enabled, once a month the plugin sends an AGGREGATED summary for the previous month: for each material or
    category, its JAN code, the monthly unit price (average, minimum, maximum), the quantity and the order count,
    together with the business type (clinic or laboratory) and the prefecture. The JAN code is what makes the
    statistics comparable across clinics; no order-level record is included.
  • Raw order records, patient information, and clinical information are NEVER sent — only the monthly aggregates above.
  • The sender identity is ANONYMOUS by default; the administrator may optionally choose to disclose it.
  • You can turn the report off at any time from the same settings screen; sending stops immediately.

Communication timing

No communication with the web service happens until the clinic administrator enables the integration (the consent
dialog on the settings screen). After consent, most communication happens in response to explicit user actions; a few
background tasks also run on a schedule, as listed below.

User actions:

  • Connecting the site: when the administrator enters the connection key, it is exchanged with the web service for an access token, and that token is renewed when it expires
  • Connecting the site with an application code instead of a connection key: the code is exchanged with the web service for the connection credentials
  • “Connection test” button: fetches one material master record to confirm the site can reach the web service
  • Material lookup and registration: when a JAN code or in-store code is entered
  • Inventory list view: refreshes status if the cached data is older than 24 hours
  • Material detail view: refreshes status if the cached data is older than 24 hours
  • “Refresh from server” button: forces an immediate status refresh
  • Order request creation: fetches recommended supplier, discontinued flag, and name changes
  • Order approval: records the order click on the web service
  • Seller linking: when a seller code is entered, links/unlinks a registered seller
  • Order to a linked seller: sends the order request to the seller via the web service
  • Material feedback: when the user posts a request, suggestion, or defect report about a material, the post is sent to the web service so it can reach the material’s manufacturer
  • Campaign/event digest: while a staff member views the My Page screen, a conditional GET (ETag) asks the web service whether the list of active supplier campaigns has changed

Scheduled background tasks (only after consent):

  • Seller order sync (hourly): checks the web service for status updates (confirmed/shipped/declined) on orders sent to linked sellers
  • Material feedback sync (hourly): checks the web service for the read/handled status of feedback the clinic has posted, and re-sends any feedback post that previously failed to reach it
  • Monthly statistics report (opt-in only, separate from consent): sends aggregated price statistics for the previous month on a monthly schedule

Three further scheduled tasks run entirely on your own site and never contact the web service: the daily stock/expiry alert check, the weekly recommendation pass, and a daily cleanup of unattached image uploads.

Data sent on user actions: corporation ID, JAN code, in-store code, (at order time) quantity and chosen supplier, (for seller orders) the order request lines, each with JAN code, product name, quantity and unit price, and (for material feedback) the post category, title, free-text body, the JAN code and manufacturer name of the material, the anonymous/urgent flags, and the clinic’s name and contact email.
Data sent by the opt-in monthly report: for each material or category, the monthly average/minimum/maximum unit price, quantity and order count, along with the material’s JAN code, the business type, and the prefecture.
Data NOT sent: patient information, clinical information, or personal health information. The opt-in monthly report never sends raw order records — only the aggregates above.

Bundled third-party libraries

Every bundled third-party file is copied verbatim, byte for byte, from the prebuilt distribution of a pinned upstream npm package. Nothing is minified, transformed, concatenated or patched by this plugin. One of the three (Chart.js) is minified in the form upstream publishes it; the human-readable source of every file is named below. The exact versions are pinned in package.json.

To verify this, run the following from the plugin’s root directory. It checks the SHA-256 of every bundled file and exits non-zero on any mismatch:

shasum -a 256 -c public/licenses/VENDOR-CHECKSUMS.txt

public/licenses/VENDOR-PROVENANCE.txt lists, for each bundled file, its upstream project, license, the exact path it was copied from, and the commands to regenerate it from npm.

  • @zxing/library 0.23.0 (Apache-2.0 License) — https://github.com/zxing-js/library — public/js/vendor/zxing-library.js, the pure-JavaScript barcode decoder used by the staff PWA. This is upstream’s unminified UMD build (umd/index.js), shipped as-is so the code running in the browser is human-readable; the TypeScript source is https://github.com/zxing-js/library/tree/v0.23.0/src. No WebAssembly and no external request is involved. License text: public/licenses/ZXING-JS-LICENSE.txt.
  • Chart.js 4.5.1 (MIT License) — https://github.com/chartjs/Chart.js — admin-side expense and price charts (public/js/vendor/chart.umd.min.js). Upstream ships this file minified; the human-readable source is dist/chart.js in the same npm package, and the original source is in the repository above.
  • qrcode-generator 2.0.4 (MIT License) — https://github.com/kazuhikoarase/qrcode-generator — renders the staff-app login QR code locally in the browser (public/js/vendor/qrcode-generator.js); no external request is made. Unminified upstream source.

Privacy Policy

  • Data sent to the web service: corporation ID and corporation name, the clinic’s contact email, JAN/in-store codes looked up, order details (JAN code, product name, quantity, unit price) for orders placed with a linked seller, and the full text of any material feedback the user chooses to post.
  • Data sent only when the optional monthly report is enabled: per material/category, the JAN code and the aggregated monthly price statistics (average/minimum/maximum unit price), quantity and order count, plus the business type (clinic or laboratory) and the prefecture. The sender identity is anonymous by default.
  • Data NOT sent: patient information, clinical information, or personal health information. The optional monthly report never sends raw order records.
  • All data is managed under the privacy policy at https://healthbanks.org/dzm/privacy (terms of use: https://healthbanks.org/dzm/terms).

螢幕擷圖

安裝方式

  1. Upload the plugin and activate it.
  2. Open “DentalZaikoMaker > Settings” in the WordPress admin.
  3. Accept the external service consent dialog.
  4. Paste the connection key issued for your account (it begins with “dzmk_”) into the connection key field and save. The corporation ID and API token are read from it automatically.
  5. Register your corporation, branches, and staff in order.

常見問題集

Does this plugin handle patient data?

No. The plugin only manages dental material inventory and never touches patient or clinical records.

Can I disconnect the external service?

Yes. The settings screen has a “連携を解除する” (revoke) button. It deletes the stored consent and the saved access tokens, and from that moment the plugin makes no request to the web service at all — including the scheduled tasks. Your local inventory data is not deleted. You can consent again later and reconnect with your connection key.

Does the plugin send any data automatically?

Only if you opt in. By default the plugin communicates only in response to your actions. There is one optional feature — the monthly statistics report — which, after you explicitly enable it, sends an aggregated monthly price summary (never raw orders or patient data) on a monthly schedule. It is anonymous by default and can be turned off at any time from the “Public report” settings screen.

Can it be used on smartphones?

Yes. Administrators can share the staff PWA URL so staff members can run inventory operations and barcode scans from a mobile browser. Camera scanning works out of the box — the decoder is bundled with the plugin and there is nothing to install (see “Camera scanning”). It needs a browser that grants camera access; codes can always be typed in by hand instead.

Does it work offline?

The plugin includes Service Worker offline caching, so the staff app opens and the inventory can be read without a connection. Changes (stock in/out, orders) still need a connection — they are not queued for later.

Does it integrate with freee or Money Forward?

Not directly, but the plugin exports CSV files in both the freee and Money Forward formats.

使用者評論

這個外掛目前沒有任何使用者評論。

參與者及開發者

以下人員參與了開源軟體〈DentalZaikoMaker〉的開發相關工作。

參與者

將〈DentalZaikoMaker〉外掛本地化為台灣繁體中文版

對開發相關資訊感興趣?

任何人均可瀏覽程式碼、查看 SVN 存放庫,或透過 RSS 訂閱開發記錄

變更記錄

1.26.0

  • Saving the connection settings now runs the connection test in the same step, so the status card reflects the result immediately instead of asking for a separate test.
  • Notification e-mails can be sent to several addresses (one per line or comma-separated), and the daily check can be switched between daily and weekly.
  • The staff PWA login is now rate-limited: repeated failures from the same address or IP are locked out for a short period. Newly set or changed PINs must be six digits; existing shorter PINs can still sign in.
  • Security: the API token is no longer read from the plaintext pending option after setup — any leftover plaintext is migrated to encrypted storage on use. The audit log no longer stores the PIN hash. When AUTH_KEY is undefined, a site-specific random salt is generated for token encryption instead of a shared constant.
  • Camera scanning now prompts to type the code in by hand when nothing is read for a while, since some small packages carry a barcode the bundled decoder cannot read.
  • CSV import recognises a “supplier product code” column, so materials keyed by a supplier’s own code rather than a JAN code are matched to the same material on re-import.

1.25.0

  • Merges the WordPress.org review work (1.21.6) with the features released to clinics in 1.22.x–1.24.0, so both lines are one code base again.
  • Supplier records now carry a corporate number (13 digits) and an office name. Entering a corporate number looks up the companion web service and links a registered distributor automatically.
  • The daily low-stock e-mail now carries a link to the reorder list, and ordering marks the material as “on order” so the e-mail stops until the stock is received (it is released automatically on stock-in, or after 30 days).
  • The reorder list can pause a material’s alert or change its reorder point directly.
  • Notification list: search, filter, sort and pagination.
  • Supplier saving moved to admin-post, so it no longer depends on output buffering.
  • Fix: activating the plugin on a site that already had data stamped the database version to the newest value without running the version-specific migrations, so any migration step that dbDelta cannot express was skipped for good. Activation now goes through the same upgrade path as a normal update. In practice this meant the supplier table kept its old unique key, so a supplier could not be registered twice under the same name with different office names — which is exactly what the corporate-number release added the office name for.
  • Readme fix: the feature list said the plugin has “three role tiers: director, staff lead, and staff”. No such roles exist. The three roles are administrator, staff and view-only, as the plugin’s own screens have always said. There is no “staff lead” role, and the “view-only” role was missing from the list.
  • The product category grouping on the stock list now matches the companion web service. The “medicine” group is relabelled from “歯科用医薬品” to “医薬品”, 68 keywords the web service had learned since this classifier was written are applied (bone graft materials, radiographic accessories, laboratory casting supplies, hand instruments and others), and a material whose generic name is empty is now grouped by its product name instead of being left unclassified. The same material could previously appear under one group here and another one on the web.

1.21.6

  • Camera scanning no longer uses WebAssembly. The decoder is now the bundled pure-JavaScript @zxing/library, so the package contains no binary, has no optional component to install, and references no external host. It reads JAN/EAN, GS1-128, GS1 DataBar, GS1 DataBar Expanded, DataMatrix and QR. GS1 DataBar Limited is no longer decoded by the camera and is typed in by hand.
  • Security fix: the staff PWA’s offline snapshot endpoint returned stock summaries for every active branch to any signed-in PWA user. It is now authorised per branch, like every other screen, so staff and view-only accounts see only their own branch.
  • Security fix: the staff PWA’s stock use/dispose handler authorised the submitted branch but then loaded the lot by its id alone, so a staff account could decrement or dispose a lot belonging to a branch it has no access to. The lot is now always looked up bound to the already-authorised branch and material.
  • Security fix: when registering a material from the staff PWA, the submitted image attachment id was trusted as-is. It is now accepted only if it is a pending upload made by the same operator.
  • The PWA’s stylesheet and page scripts are now served directly from the plugin directory with wp_enqueue_style/wp_enqueue_script instead of being streamed through a PHP route. The Service Worker is still returned by PHP, because it needs the Service-Worker-Allowed header to take /dzm-pwa/ as its scope.
  • Login to the staff PWA is now CSRF-protected with a nonce, and all display parameters on PWA screens are read through registered query vars or a nonce-checked helper rather than from $_GET inside the templates.
  • Input from $_POST is sanitized where it is read (email with sanitize_email, and so on) instead of later on save.
  • The plugin no longer calls base64_encode() or base64_decode() anywhere. The encrypted API token is stored as hexadecimal, and the connection key’s base64url payload is decoded with PHP’s own sodium_base642bin().
  • New: the settings screen now has a “revoke” button that disables the external service integration. It deletes the stored consent and access tokens, after which the plugin issues no API request at all — including the scheduled tasks. The plugin’s own screens and readme said this was possible, but no such control existed. Local inventory data is kept.
  • Fixed: redeeming an application code contacted the web service before consent had been given. It is now gated on consent like every other request, so the plugin really does send nothing until the administrator agrees.
  • Fixed: on the “needs reordering” list, the link to the order screen passed the suggested quantity under an unregistered query name, so the order form always opened with a quantity of 1 instead of the shortfall.
  • UPC-E was listed as a supported camera format, but the bundled decoder cannot read it, and a UPC-E code that did decode would have been handed to the JAN field in its compressed 8-digit form, matching nothing. It has been removed from the enabled formats and from the documentation. UPC-A, EAN-13 and EAN-8 are unaffected.
  • Documentation: corrected several statements in readme.txt that no longer matched the code — the weekly recommendation pass runs three detectors, not four; there is no scheduled manufacturer-list refresh; the campaign digest runs on page view, not on a schedule; offline changes are not queued for later sync; and the lists of what is sent to the web service now name every field, including the material feedback body and the clinic’s contact details.

1.21.5

  • Security: all admin-screen GET parameters (display filters, edit links and post-redirect status flags) are now nonce-verified through a single helper; without a valid nonce the screen renders its default state. In the staff PWA, which authenticates with its own JWT session rather than WordPress cookies, the post-redirect banner parameters and the scan hand-off parameters are verified with an equivalent CSRF check before they are read.
  • Security: fixed a branch authorization gap on the staff app home screen. The branch given in the query string was used without checking the operator’s permission, so a staff or viewer account could read another branch’s stock summary by changing it. The branch is now re-authorized with the same check used by the other screens, and the branch switcher is shown only to operators who are allowed to cross branches.
  • The one-time dzm_* to dzmaker_* option migration now renames only this plugin’s own options from a fixed list of known names instead of scanning wp_options with a LIKE ‘dzm_%’ pattern, so options belonging to other plugins can never be read, renamed or deleted by it.
  • Removed a require of wp-admin/includes/media.php whose functions were never used (wp_handle_sideload and wp_generate_attachment_metadata come from file.php and image.php, which are still loaded and used immediately).
  • Uninstall additionally removes two API response cache options.

1.21.4

  • Database: every query now passes its table name to $wpdb->prepare() through the %i identifier placeholder instead of interpolating it into the SQL string. This covers all 117 call sites across 32 files, including SHOW COLUMNS, ALTER TABLE and DROP TABLE. Because %i requires WordPress 6.2, “Requires at least” is raised from 6.0 to 6.2.
  • Code quality: removed all 286 file-level phpcs:disable comments. The plugin now reports zero errors and zero warnings under PHPCS with the WordPress Coding Standards sniffs that guard database access, request handling, escaping, i18n, prefixing and PHP/WP compatibility (WordPress.DB, WordPress.Security, WordPress.WP.*, WordPress.NamingConventions.PrefixAllGlobals, PHPCompatibilityWP); the remaining suppressions are all line-scoped and each carries a written justification. The phpcs.xml.dist and composer.json that reproduce this run are in the plugin’s source repository (they are development tooling and are not part of the distributed package).
  • Fixed: four call sites queried wp_dzmaker_corporations / wp_dzmaker_suppliers, tables that do not exist — the real tables are wp_dzm_*. As a result the supplier master and seller-linking features always read corporation ID 0, attaching a linked seller from the staff app always failed, and the 1.2.0 database migration silently skipped its supplier seeding and back-fill step.
  • Fixed: public/js/vendor/qrcode-generator.js had one line altered from the upstream source (the x and y arguments of renderTo2dContext() were swapped). Restored to the unmodified upstream 2.0.4 file.
  • Fixed: the overstock recommendation summed a column that does not exist in the inventory log table, so it errored on every weekly run and never produced a recommendation. It now works.
  • The volume-discount and unused-event recommendations are disabled. They queried supplier columns that were never part of the database schema and nothing ever wrote them, so they have never produced a result; they now no longer log a database error on the weekly cron. Nothing that previously worked has been removed.
  • Bundled libraries are now pinned in package.json, copied verbatim from upstream, and verifiable against public/licenses/VENDOR-CHECKSUMS.txt with shasum -a 256 -c from the plugin’s root directory.
  • Fixed: uninstalling the plugin left two of its twenty database tables behind (..._dzm_linked_sellers and ..._dzm_material_feedback); they were never listed in the uninstaller. All twenty are now removed.
  • Admin and PWA view templates now prefix their local variables with dzmaker_, which also resolves 28 collisions with WordPress core global variable names.

1.21.3

  • Security: branch-level authorization is now enforced for the My Page inventory view and the PWA stock/order actions. Staff and view-only accounts are restricted to their own assigned branch; only administrators can switch branches. Previously the branch could be changed via a request parameter without a branch-level permission check.
  • Privacy/uninstall: the uninstaller now deletes only this plugin’s own known options by explicit name (both the current dzmaker_ and legacy dzm_ prefixes). It no longer runs a broad LIKE 'dzm_%' sweep, so options belonging to other plugins are never touched.
  • Hardening: added explicit annotations to the remaining direct database reads (table-name-only queries with no user input; all user-supplied values already go through prepared statements).

1.21.2

  • Compliance: renamed the plugin’s internal code prefix from the 3-character “dzm” / “DZM_” to the 4+ character “dzmaker” / “DZMAKER_” across all constants, classes, options, transients, cron hooks, the my-page shortcode, and JavaScript globals, per the WordPress.org unique-prefix requirement. Existing installations are migrated automatically on upgrade (options, scheduled tasks, and the my-page shortcode are carried over; database tables are unchanged, so no data is lost).
  • Fix: updated the Author URI to a fast, reliable address.

1.21.1

  • Compliance: moved all inline <script> / <style> blocks in admin and front-end views into enqueued asset files (wp_enqueue_script / wp_enqueue_style / wp_add_inline_style / wp_localize_script). No functional change.
  • Updated the bundled Chart.js library to 4.5.1.

1.21.0

  • New: post requests / suggestions about a registered material to its manufacturer. The 使用材料一覧 (used-materials list) gains a 「投稿」 button per material; the form (with a clear purpose/policy notice, an anonymous toggle, and an “urgent defect” checkbox with an important-notice alert) shows a confirmation screen with 戻る/送信 before sending.
  • New: 投稿済一覧 (posted list) screen with progress badges (緊急 / 通知済 / メーカー未開封 / 開封済). “メーカー未開封” means the manufacturer is either not registered in the system or has not opened the post yet.
  • The post is stored on the Web side (system of record). Paid manufacturers can view posts limited to their own products on their Web mypage, and receive an email notification linking to the post. Urgent defects additionally notify the system administrator and (when configured) the dental associations.
  • Hourly sync pulls the manufacturer “opened” / notification status back into the posted list. DB schema 1.7.0 1.8.0 (adds material_feedback).
  • Used-materials list: the per-row 操作 / 投稿 buttons are now laid out side by side without arrows (投稿 is navy). Pressing 操作 now smooth-scrolls to a clearly highlighted operation panel (with a “back to list” link and a short description of what you can do there), and a one-line hint above the table explains both buttons.
  • New: a Top (機能一覧) landing page placed at the very top of the admin menu. It presents every feature as a styled, categorized card (title + description) that links straight to the feature page. The former home (initial-setup steps + staff-app QR) is renamed to 「はじめに」 and kept directly below Top.

1.20.2

  • Data import (CSV): the JAN code column now accepts GS1 codes too — a GS1 element string (e.g. “(01)…(17)…(10)…” or raw GS1-128/DataMatrix) is parsed to extract the GTIN (matched as a JAN) and, when the expiry / lot columns are blank, the expiry date (AI 17) and lot number (AI 10) are auto-filled.
  • Import template: removed the “仕入先URL” column from the sample to keep it simple (the column is still accepted if present).

1.20.1

  • Data import (CSV) screen: the “open the Web version” link now goes directly to the Web matching page (/clinic/import); after logging in there, the clinic lands straight on the import page.

1.20.0

  • New: bulk CSV import for onboarding (“データ取り込み(CSV)” screen). Import past inventory and purchase history at once instead of entering items one by one. Required columns are just product name and quantity; a downloadable template is provided; UTF-8 and Shift_JIS are both accepted; items without a JAN code are supported (matched by in-store code or product name, or created as custom). A preview shows per-row status before committing.
  • Two import modes (auto-detected per row): opening balance (current on-hand stock, not counted as an expense) and purchase history (past purchases with date/price, fed into the expense dashboard and price trend, without inflating current stock — no double counting).
  • Guidance to optionally match official product names on the Web side first, then import the cleaned CSV. Added an optional onboarding step.

For the changelog of releases earlier than 1.20.0, see changelog.txt included with the plugin.