Add a printer job queue via the printer plugin
This commit is contained in:
parent
63e1541aa3
commit
c955a6ee40
24 changed files with 561 additions and 1 deletions
1
Gemfile
1
Gemfile
|
@ -64,6 +64,7 @@ gem 'foodsoft_discourse', path: 'plugins/discourse'
|
||||||
|
|
||||||
# plugins not enabled by default
|
# plugins not enabled by default
|
||||||
#gem 'foodsoft_current_orders', path: 'plugins/current_orders'
|
#gem 'foodsoft_current_orders', path: 'plugins/current_orders'
|
||||||
|
#gem 'foodsoft_printer', path: 'plugins/printer'
|
||||||
#gem 'foodsoft_uservoice', path: 'plugins/uservoice'
|
#gem 'foodsoft_uservoice', path: 'plugins/uservoice'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -649,6 +649,11 @@ de:
|
||||||
pdf_font_size: Schriftgrösse
|
pdf_font_size: Schriftgrösse
|
||||||
pdf_page_size: Seitenformat
|
pdf_page_size: Seitenformat
|
||||||
price_markup: Foodcoop Marge
|
price_markup: Foodcoop Marge
|
||||||
|
printer_print_order_articles: Artikel PDF drucken
|
||||||
|
printer_print_order_fax: Fax PDF drucken
|
||||||
|
printer_print_order_groups: Gruppen PDF drucken
|
||||||
|
printer_print_order_matrix: Matrix PDF drucken
|
||||||
|
printer_token: Geheimer Token
|
||||||
stop_ordering_under: Apfelpunkte Minimum
|
stop_ordering_under: Apfelpunkte Minimum
|
||||||
tasks_period_days: Zeitintervall
|
tasks_period_days: Zeitintervall
|
||||||
tasks_upfront_days: Im Voraus
|
tasks_upfront_days: Im Voraus
|
||||||
|
@ -662,6 +667,7 @@ de:
|
||||||
use_iban: IBAN verwenden
|
use_iban: IBAN verwenden
|
||||||
use_messages: Nachrichten
|
use_messages: Nachrichten
|
||||||
use_nick: Benutzernamen verwenden
|
use_nick: Benutzernamen verwenden
|
||||||
|
use_printer: Drucker verwenden
|
||||||
use_wiki: Wiki verwenden
|
use_wiki: Wiki verwenden
|
||||||
webstats_tracking_code: Code für Websiteanalysetool
|
webstats_tracking_code: Code für Websiteanalysetool
|
||||||
tabs:
|
tabs:
|
||||||
|
@ -1109,6 +1115,8 @@ de:
|
||||||
submit:
|
submit:
|
||||||
invite:
|
invite:
|
||||||
create: Einladung verschicken
|
create: Einladung verschicken
|
||||||
|
printer_job:
|
||||||
|
create: Druckauftrag erstellen
|
||||||
message:
|
message:
|
||||||
create: Nachricht verschicken
|
create: Nachricht verschicken
|
||||||
tasks:
|
tasks:
|
||||||
|
@ -1461,6 +1469,7 @@ de:
|
||||||
manage: Bestellverwaltung
|
manage: Bestellverwaltung
|
||||||
ordering: Bestellen!
|
ordering: Bestellen!
|
||||||
pickups: Abholtage
|
pickups: Abholtage
|
||||||
|
printer_jobs: Druckaufträge
|
||||||
title: Bestellungen
|
title: Bestellungen
|
||||||
tasks: Aufgaben
|
tasks: Aufgaben
|
||||||
wiki:
|
wiki:
|
||||||
|
@ -1565,6 +1574,7 @@ de:
|
||||||
confirm_end: |-
|
confirm_end: |-
|
||||||
Willst Du wirklich die Bestellung %{order} beenden?
|
Willst Du wirklich die Bestellung %{order} beenden?
|
||||||
Es gibt kein zurück.
|
Es gibt kein zurück.
|
||||||
|
confirm_create_printer_job: Für diese Bestellung wurde bereits ein Druckauftrag erstellt. Willst du einen neuen erstellen?
|
||||||
confirm_send_to_supplier: Die Bestellung wurde bereit am %{when} zur Lieferantin geschickt. Willst du sie wirklich erneut schicken?
|
confirm_send_to_supplier: Die Bestellung wurde bereit am %{when} zur Lieferantin geschickt. Willst du sie wirklich erneut schicken?
|
||||||
create_invoice: Rechnung anlegen
|
create_invoice: Rechnung anlegen
|
||||||
description1_order: "%{state} Bestellung von %{supplier} angelegt von %{who},"
|
description1_order: "%{state} Bestellung von %{supplier} angelegt von %{who},"
|
||||||
|
@ -1704,6 +1714,18 @@ de:
|
||||||
nojs: Achtung, Cookies und Javascript müssen aktiviert sein! %{link} bitte abschalten.
|
nojs: Achtung, Cookies und Javascript müssen aktiviert sein! %{link} bitte abschalten.
|
||||||
noscript: NoScript
|
noscript: NoScript
|
||||||
title: Foodsoft anmelden
|
title: Foodsoft anmelden
|
||||||
|
printer_jobs:
|
||||||
|
create:
|
||||||
|
notice: "%{count} Druckaufträge wurden erstellt."
|
||||||
|
destroy:
|
||||||
|
notice: Druckauftrag wurde gelöscht.
|
||||||
|
index:
|
||||||
|
finished: Beendet
|
||||||
|
pending: Ausstehend
|
||||||
|
queued: Warten auf den Bestellschluss
|
||||||
|
title: Druckaufträge
|
||||||
|
show:
|
||||||
|
title: Druckauftrag %{id}
|
||||||
shared:
|
shared:
|
||||||
articles:
|
articles:
|
||||||
ordered: Bestellt
|
ordered: Bestellt
|
||||||
|
|
|
@ -652,6 +652,11 @@ en:
|
||||||
pdf_font_size: Font size
|
pdf_font_size: Font size
|
||||||
pdf_page_size: Page size
|
pdf_page_size: Page size
|
||||||
price_markup: Foodcoop margin
|
price_markup: Foodcoop margin
|
||||||
|
printer_print_order_articles: Print article PDF
|
||||||
|
printer_print_order_fax: Print fax PDF
|
||||||
|
printer_print_order_groups: Print group PDF
|
||||||
|
printer_print_order_matrix: Print matrix PDF
|
||||||
|
printer_token: Secret token
|
||||||
stop_ordering_under: Minimum apple points
|
stop_ordering_under: Minimum apple points
|
||||||
tasks_period_days: Period
|
tasks_period_days: Period
|
||||||
tasks_upfront_days: Create upfront
|
tasks_upfront_days: Create upfront
|
||||||
|
@ -665,6 +670,7 @@ en:
|
||||||
use_iban: Use IBAN
|
use_iban: Use IBAN
|
||||||
use_messages: Messages
|
use_messages: Messages
|
||||||
use_nick: Use nicknames
|
use_nick: Use nicknames
|
||||||
|
use_printer: Use printer
|
||||||
use_wiki: Enable wiki
|
use_wiki: Enable wiki
|
||||||
webstats_tracking_code: Tracking code
|
webstats_tracking_code: Tracking code
|
||||||
tabs:
|
tabs:
|
||||||
|
@ -1126,6 +1132,8 @@ en:
|
||||||
submit:
|
submit:
|
||||||
invite:
|
invite:
|
||||||
create: send invitation
|
create: send invitation
|
||||||
|
printer_job:
|
||||||
|
create: create printer job
|
||||||
message:
|
message:
|
||||||
create: send message
|
create: send message
|
||||||
tasks:
|
tasks:
|
||||||
|
@ -1487,6 +1495,7 @@ en:
|
||||||
manage: Manage orders
|
manage: Manage orders
|
||||||
ordering: Place order!
|
ordering: Place order!
|
||||||
pickups: Pickup days
|
pickups: Pickup days
|
||||||
|
printer_jobs: Printer jobs
|
||||||
title: Orders
|
title: Orders
|
||||||
tasks: Tasks
|
tasks: Tasks
|
||||||
wiki:
|
wiki:
|
||||||
|
@ -1591,6 +1600,7 @@ en:
|
||||||
confirm_end: |-
|
confirm_end: |-
|
||||||
Do you really want to close the order %{order}?
|
Do you really want to close the order %{order}?
|
||||||
There is no going back.
|
There is no going back.
|
||||||
|
confirm_create_printer_job: A printer job for this order has been created already. Do you want to create a new one?
|
||||||
confirm_send_to_supplier: The order has been sent to the supplier already on %{when}. Do you really want to send it again?
|
confirm_send_to_supplier: The order has been sent to the supplier already on %{when}. Do you really want to send it again?
|
||||||
create_invoice: Add invoice
|
create_invoice: Add invoice
|
||||||
description1_order: "%{state} order from %{supplier} opened by %{who},"
|
description1_order: "%{state} order from %{supplier} opened by %{who},"
|
||||||
|
@ -1730,6 +1740,18 @@ en:
|
||||||
nojs: Attention, cookies and javascript have to be activated! Please switch off %{link}.
|
nojs: Attention, cookies and javascript have to be activated! Please switch off %{link}.
|
||||||
noscript: NoScript
|
noscript: NoScript
|
||||||
title: Foodsoft login
|
title: Foodsoft login
|
||||||
|
printer_jobs:
|
||||||
|
create:
|
||||||
|
notice: Created %{count} printer jobs.
|
||||||
|
destroy:
|
||||||
|
notice: Printer job has been deleted.
|
||||||
|
index:
|
||||||
|
finished: Finished
|
||||||
|
pending: Pending
|
||||||
|
queued: Waiting for order to close
|
||||||
|
title: Printer jobs
|
||||||
|
show:
|
||||||
|
title: Printer job %{id}
|
||||||
shared:
|
shared:
|
||||||
articles:
|
articles:
|
||||||
ordered: Ordered
|
ordered: Ordered
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
class CreatePrinterJobs < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :printer_jobs do |t|
|
||||||
|
t.references :order
|
||||||
|
t.string :document, null: false
|
||||||
|
t.integer :created_by_user_id, null: false
|
||||||
|
t.integer :finished_by_user_id
|
||||||
|
t.datetime :finished_at, index: true
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table :printer_job_updates do |t|
|
||||||
|
t.references :printer_job, null: false
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
t.string :state, null: false
|
||||||
|
t.text :message
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :printer_job_updates, [:printer_job_id, :created_at]
|
||||||
|
end
|
||||||
|
end
|
21
db/schema.rb
21
db/schema.rb
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20171201000000) do
|
ActiveRecord::Schema.define(version: 20181201000000) do
|
||||||
|
|
||||||
create_table "article_categories", force: :cascade do |t|
|
create_table "article_categories", force: :cascade do |t|
|
||||||
t.string "name", limit: 255, default: "", null: false
|
t.string "name", limit: 255, default: "", null: false
|
||||||
|
@ -378,6 +378,25 @@ ActiveRecord::Schema.define(version: 20171201000000) do
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "printer_job_updates", force: :cascade do |t|
|
||||||
|
t.integer "printer_job_id", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.string "state", null: false
|
||||||
|
t.text "message"
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "printer_job_updates", ["printer_job_id", "created_at"], name: "index_printer_job_updates_on_printer_job_id_and_created_at", using: :btree
|
||||||
|
|
||||||
|
create_table "printer_jobs", force: :cascade do |t|
|
||||||
|
t.integer "order_id"
|
||||||
|
t.string "document", null: false
|
||||||
|
t.integer "created_by_user_id", null: false
|
||||||
|
t.integer "finished_by_user_id"
|
||||||
|
t.datetime "finished_at"
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "printer_jobs", ["finished_at"], name: "index_printer_jobs_on_finished_at", using: :btree
|
||||||
|
|
||||||
create_table "settings", force: :cascade do |t|
|
create_table "settings", force: :cascade do |t|
|
||||||
t.string "var", limit: 255, null: false
|
t.string "var", limit: 255, null: false
|
||||||
t.text "value", limit: 65535
|
t.text "value", limit: 65535
|
||||||
|
|
61
plugins/printer/README.md
Normal file
61
plugins/printer/README.md
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
FoodsoftPrinter
|
||||||
|
=================
|
||||||
|
|
||||||
|
This plugin adds a printer queue to allow mebers to print PDF with one click.
|
||||||
|
Usually a mini computer with a printer at a room in the foodcoop will wait for
|
||||||
|
new printer jobs and prints them.
|
||||||
|
|
||||||
|
This plugin is not enabled by default. To install it, add uncomment the
|
||||||
|
corresponding line in the `Gemfile`, or add:
|
||||||
|
|
||||||
|
```Gemfile
|
||||||
|
gem 'foodsoft_printer', path: 'plugins/foodsoft_printer'
|
||||||
|
```
|
||||||
|
|
||||||
|
This plugin introduces the foodcoop config option `printer_token`, which takes
|
||||||
|
a random string for authentication at the endpoint. Additionally a set of
|
||||||
|
PDF files can be selected, which will be generated when a print is triggered.
|
||||||
|
|
||||||
|
The communication with the printer client happens via two endpoints, which both
|
||||||
|
require the `printer_token` as `Bearer` token in the `Authorization` header.
|
||||||
|
* `/:foodcoop/printer/socket`: main WebSocket communication
|
||||||
|
* `/:foodcoop/printer/:id`: HTTP GET for documents
|
||||||
|
|
||||||
|
The main communication happens via JSON messages via an WebSocket connection,
|
||||||
|
which sends an array of docuement ids to the client, which need to be printed.
|
||||||
|
Addionally the docuemnt can be downloaded as PDF via a separate endpoint.
|
||||||
|
The client can updated the status of the documents by sending an object with
|
||||||
|
the following keys to the server:
|
||||||
|
* `id` (NBR, REQUIRED): id of the document, which should be updated
|
||||||
|
* `state` (ENUM): the current sate of the printing progress.
|
||||||
|
* `message` (STR, REQUIRES `state`): detailed description of the current state
|
||||||
|
(e.g. download progress)
|
||||||
|
* `finish` (BOOL): when set to `true` the job will be marked as done
|
||||||
|
|
||||||
|
The following values are valid for the `state` property:
|
||||||
|
* `queued`: the document is not yet ready for printing
|
||||||
|
* `ready`: the document is ready to be downloaded
|
||||||
|
* `downloading`: transfer of the document is in progress
|
||||||
|
* `pending`: download completed, waiting for the printer
|
||||||
|
* `held`: e.g., for "PIN printing"
|
||||||
|
* `processing`: printing is in progress
|
||||||
|
* `stopped`: out of paper, etc.)
|
||||||
|
* `cancelled`: the user stopped the action
|
||||||
|
* `aborted`: the printer stopped the action
|
||||||
|
* `completed`: print was successful
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
A server sending `{"unfinished_jobs":[12,16]}` via WebSocket indicates that the
|
||||||
|
two documents `12` and `16` are ready for printing. The client will request the
|
||||||
|
first document from `/foodcoop/printer/12` and send it to the printer. The
|
||||||
|
status can be updated by sending `{"id":12,"state":"pending"}` via WebSocket to
|
||||||
|
the server. Sending `{"id":12,"state":"completed","finish":true}` as soon as
|
||||||
|
when the printing succeded will mark the job done.
|
||||||
|
|
||||||
|
To use this plugin the webserver must support WebSockets. The current
|
||||||
|
implementation uses socket hijack, which is not supported by `thin`. `puma`
|
||||||
|
supports that, but might lead to other problems, since it's not well tested
|
||||||
|
in combination with foodsoft. Please be careful when switching the webserver!
|
||||||
|
|
||||||
|
This plugin is part of the foodsoft package and uses the AGPL-3 license (see
|
||||||
|
foodsoft's LICENSE for the full license text).
|
40
plugins/printer/Rakefile
Normal file
40
plugins/printer/Rakefile
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env rake
|
||||||
|
begin
|
||||||
|
require 'bundler/setup'
|
||||||
|
rescue LoadError
|
||||||
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
||||||
|
end
|
||||||
|
begin
|
||||||
|
require 'rdoc/task'
|
||||||
|
rescue LoadError
|
||||||
|
require 'rdoc/rdoc'
|
||||||
|
require 'rake/rdoctask'
|
||||||
|
RDoc::Task = Rake::RDocTask
|
||||||
|
end
|
||||||
|
|
||||||
|
RDoc::Task.new(:rdoc) do |rdoc|
|
||||||
|
rdoc.rdoc_dir = 'rdoc'
|
||||||
|
rdoc.title = 'FoodsoftPrinter'
|
||||||
|
rdoc.options << '--line-numbers'
|
||||||
|
rdoc.rdoc_files.include('README.rdoc')
|
||||||
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||||
|
end
|
||||||
|
|
||||||
|
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
||||||
|
load 'rails/tasks/engine.rake'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Bundler::GemHelper.install_tasks
|
||||||
|
|
||||||
|
require 'rake/testtask'
|
||||||
|
|
||||||
|
Rake::TestTask.new(:test) do |t|
|
||||||
|
t.libs << 'lib'
|
||||||
|
t.libs << 'test'
|
||||||
|
t.pattern = 'test/**/*_test.rb'
|
||||||
|
t.verbose = false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
task :default => :test
|
58
plugins/printer/app/controllers/printer_controller.rb
Normal file
58
plugins/printer/app/controllers/printer_controller.rb
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
class PrinterController < ApplicationController
|
||||||
|
include Concerns::SendOrderPdf
|
||||||
|
include Tubesock::Hijack
|
||||||
|
|
||||||
|
skip_before_filter :authenticate
|
||||||
|
before_filter :authenticate_printer
|
||||||
|
before_filter -> { require_plugin_enabled FoodsoftPrinter }
|
||||||
|
|
||||||
|
def socket
|
||||||
|
hijack do |tubesock|
|
||||||
|
tubesock.onopen do
|
||||||
|
tubesock.send_data unfinished_jobs
|
||||||
|
end
|
||||||
|
|
||||||
|
tubesock.onmessage do |data|
|
||||||
|
update_job data
|
||||||
|
tubesock.send_data unfinished_jobs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
job = PrinterJob.find(params[:id])
|
||||||
|
send_order_pdf job.order, job.document
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def unfinished_jobs
|
||||||
|
{
|
||||||
|
unfinished_jobs: PrinterJob.pending.map(&:id)
|
||||||
|
}.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_job(data)
|
||||||
|
json = JSON.parse data, symbolize_names: true
|
||||||
|
job = PrinterJob.unfinished.find_by_id(json[:id])
|
||||||
|
return unless job
|
||||||
|
if json[:state]
|
||||||
|
job.add_update! json[:state], json[:message]
|
||||||
|
end
|
||||||
|
job.finish! if json[:finish]
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def bearer_token
|
||||||
|
pattern = /^Bearer /
|
||||||
|
header = request.headers['Authorization']
|
||||||
|
header.gsub(pattern, '') if header && header.match(pattern)
|
||||||
|
end
|
||||||
|
|
||||||
|
def authenticate_printer
|
||||||
|
return head(:unauthorized) unless bearer_token
|
||||||
|
return head(:forbidden) if bearer_token != FoodsoftConfig[:printer_token]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
44
plugins/printer/app/controllers/printer_jobs_controller.rb
Normal file
44
plugins/printer/app/controllers/printer_jobs_controller.rb
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
class PrinterJobsController < ApplicationController
|
||||||
|
include Concerns::SendOrderPdf
|
||||||
|
|
||||||
|
before_filter -> { require_plugin_enabled FoodsoftPrinter }
|
||||||
|
|
||||||
|
def index
|
||||||
|
jobs = PrinterJob.includes(:printer_job_updates)
|
||||||
|
@pending_jobs = jobs.pending
|
||||||
|
@queued_jobs = jobs.queued
|
||||||
|
@finished_jobs = jobs.finished.order(finished_at: :desc).page(params[:page]).per(@per_page)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
order = Order.find(params[:order])
|
||||||
|
state = order.open? ? 'queued' : 'ready'
|
||||||
|
count = 0
|
||||||
|
PrinterJob.transaction do
|
||||||
|
%w(articles fax groups matrix).each do |document|
|
||||||
|
next unless FoodsoftConfig["printer_print_order_#{document}"]
|
||||||
|
job = PrinterJob.create! order: order, document: document, created_by: current_user
|
||||||
|
job.add_update! state
|
||||||
|
count += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
redirect_to order, notice: t('.notice', count: count)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@job = PrinterJob.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def document
|
||||||
|
job = PrinterJob.find(params[:id])
|
||||||
|
send_order_pdf job.order, job.document
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
job = PrinterJob.find(params[:id])
|
||||||
|
job.finish! current_user
|
||||||
|
redirect_to printer_jobs_path, notice: t('.notice')
|
||||||
|
rescue => error
|
||||||
|
redirect_to printer_jobs_path, t('errors.general_msg', msg: error.message)
|
||||||
|
end
|
||||||
|
end
|
31
plugins/printer/app/models/printer_job.rb
Normal file
31
plugins/printer/app/models/printer_job.rb
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
class PrinterJob < ActiveRecord::Base
|
||||||
|
|
||||||
|
belongs_to :order
|
||||||
|
belongs_to :created_by, class_name: 'User', foreign_key: 'created_by_user_id'
|
||||||
|
belongs_to :finished_by, class_name: 'User', foreign_key: 'finished_by_user_id'
|
||||||
|
has_many :printer_job_updates
|
||||||
|
|
||||||
|
scope :finished, -> { where.not(finished_at: nil) }
|
||||||
|
scope :unfinished, -> { where(finished_at: nil).order(:id) }
|
||||||
|
scope :pending, -> { unfinished.includes(:order).where.not(orders: {state: 'open'}) }
|
||||||
|
scope :queued, -> { unfinished.includes(:order).where(orders: {state: 'open'}) }
|
||||||
|
|
||||||
|
def last_update_at
|
||||||
|
printer_job_updates.order(:created_at).last.try(&:created_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_update_state
|
||||||
|
printer_job_updates.order(:created_at).last.try(&:state)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_update!(state, message=nil)
|
||||||
|
return unless finished_at.nil?
|
||||||
|
PrinterJobUpdate.create! printer_job: self, state: state, message: message
|
||||||
|
end
|
||||||
|
|
||||||
|
def finish!(user=nil)
|
||||||
|
return unless finished_at.nil?
|
||||||
|
update_attributes finished_at: Time.now, finished_by: user
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
5
plugins/printer/app/models/printer_job_update.rb
Normal file
5
plugins/printer/app/models/printer_job_update.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class PrinterJobUpdate < ActiveRecord::Base
|
||||||
|
|
||||||
|
belongs_to :printer_job
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
/ insert_after ':root:last-child'
|
||||||
|
= config_use_heading form, :use_printer do
|
||||||
|
= config_input form, :printer_token, as: :string, input_html: {class: 'input-xlarge'}
|
||||||
|
= config_input form, :printer_print_order_fax, as: :boolean
|
||||||
|
= config_input form, :printer_print_order_articles, as: :boolean
|
||||||
|
= config_input form, :printer_print_order_groups, as: :boolean
|
||||||
|
= config_input form, :printer_print_order_matrix, as: :boolean
|
|
@ -0,0 +1,7 @@
|
||||||
|
/ insert_after 'erb:contains("title t(\'.title\'")'
|
||||||
|
- content_for :actionbar do
|
||||||
|
- if FoodsoftPrinter.enabled?
|
||||||
|
= link_to content_tag(:i, nil, class: 'icon-print'), printer_jobs_path(order: @order), method: :post,
|
||||||
|
class: "btn#{' btn-primary' unless @order.printer_jobs.any?}",
|
||||||
|
data: {confirm: @order.printer_jobs.any? && t('.confirm_create_printer_job') },
|
||||||
|
title: t('helpers.submit.printer_job.create')
|
22
plugins/printer/app/views/printer_jobs/_jobs.html.haml
Normal file
22
plugins/printer/app/views/printer_jobs/_jobs.html.haml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
.pull-right
|
||||||
|
- if @finished_jobs.total_pages > 1
|
||||||
|
.btn-group= items_per_page wrap: false
|
||||||
|
= pagination_links_remote @finished_jobs
|
||||||
|
%table.table.table-striped
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th= heading_helper(PrinterJob, :id)
|
||||||
|
%th= heading_helper(PrinterJob, :order)
|
||||||
|
%th= heading_helper(PrinterJob, :pickup)
|
||||||
|
%th= heading_helper(PrinterJob, :document)
|
||||||
|
%th= heading_helper(PrinterJob, :last_update_state)
|
||||||
|
%th= heading_helper(PrinterJob, :finished_at)
|
||||||
|
%tbody
|
||||||
|
- @finished_jobs.each do |j|
|
||||||
|
%tr
|
||||||
|
%td= link_to j.id, j
|
||||||
|
%td= link_to j.order.supplier.name, j.order
|
||||||
|
%td= format_date j.order.pickup
|
||||||
|
%td= link_to j.document, document_printer_job_path(j)
|
||||||
|
%td= j.last_update_state
|
||||||
|
%td= format_time j.finished_at
|
47
plugins/printer/app/views/printer_jobs/index.html.haml
Normal file
47
plugins/printer/app/views/printer_jobs/index.html.haml
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
- title t('.title')
|
||||||
|
|
||||||
|
- unless @pending_jobs.empty? && @queued_jobs.empty?
|
||||||
|
.well
|
||||||
|
%h2
|
||||||
|
- if @pending_jobs.any?
|
||||||
|
=t '.pending'
|
||||||
|
- else
|
||||||
|
=t '.queued'
|
||||||
|
%table.table.table-striped
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th= heading_helper(PrinterJob, :id)
|
||||||
|
%th= heading_helper(PrinterJob, :order)
|
||||||
|
%th= heading_helper(PrinterJob, :pickup)
|
||||||
|
%th= heading_helper(PrinterJob, :document)
|
||||||
|
%th= heading_helper(PrinterJob, :last_update_at)
|
||||||
|
%th= heading_helper(PrinterJob, :last_update_state)
|
||||||
|
%th
|
||||||
|
%tbody
|
||||||
|
- @pending_jobs.each do |j|
|
||||||
|
%tr
|
||||||
|
%td= link_to j.id, j
|
||||||
|
%td= link_to j.order.supplier.name, j.order
|
||||||
|
%td= format_date j.order.pickup
|
||||||
|
%td= link_to j.document, document_printer_job_path(j)
|
||||||
|
%td= format_time j.last_update_at
|
||||||
|
%td= j.last_update_state
|
||||||
|
%td= link_to t('ui.delete'), j, method: :delete, data: {:confirm => t('ui.confirm_delete', name: j.id)},
|
||||||
|
class: 'btn btn-mini btn-danger'
|
||||||
|
- if @pending_jobs.any?
|
||||||
|
%tr
|
||||||
|
%td{colspan: 7}
|
||||||
|
%h2= t '.queued'
|
||||||
|
- @queued_jobs.each do |j|
|
||||||
|
%tr
|
||||||
|
%td= link_to j.id, j
|
||||||
|
%td= link_to j.order.supplier.name, j.order
|
||||||
|
%td= format_date j.order.pickup
|
||||||
|
%td= link_to j.document, document_printer_job_path(j)
|
||||||
|
%td= format_time j.last_update_at
|
||||||
|
%td= j.last_update_state
|
||||||
|
%td= link_to t('ui.delete'), j, method: :delete, data: {:confirm => t('ui.confirm_delete', name: j.id)},
|
||||||
|
class: 'btn btn-mini btn-danger'
|
||||||
|
|
||||||
|
%h2= t '.finished'
|
||||||
|
#jobsTable= render 'jobs'
|
1
plugins/printer/app/views/printer_jobs/index.js.haml
Normal file
1
plugins/printer/app/views/printer_jobs/index.js.haml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
$('#jobsTable').html('#{j(render('jobs'))}');
|
36
plugins/printer/app/views/printer_jobs/show.html.haml
Normal file
36
plugins/printer/app/views/printer_jobs/show.html.haml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
- title t('.title', id: @job.id)
|
||||||
|
|
||||||
|
%p
|
||||||
|
%b= heading_helper(PrinterJob, :created_by) + ':'
|
||||||
|
= show_user @job.created_by
|
||||||
|
%p
|
||||||
|
%b= heading_helper(PrinterJob, :order) + ':'
|
||||||
|
= @job.order.supplier.name
|
||||||
|
%p
|
||||||
|
%b= heading_helper(PrinterJob, :pickup) + ':'
|
||||||
|
= @job.order.pickup
|
||||||
|
%p
|
||||||
|
%b= heading_helper(PrinterJob, :document) + ':'
|
||||||
|
= @job.document
|
||||||
|
- if @job.finished_at
|
||||||
|
%p
|
||||||
|
%b= heading_helper(PrinterJob, :finished_at) + ':'
|
||||||
|
= format_time @job.finished_at
|
||||||
|
- if @job.finished_by
|
||||||
|
%p
|
||||||
|
%b= heading_helper(PrinterJob, :finished_by) + ':'
|
||||||
|
= show_user @job.finished_by
|
||||||
|
|
||||||
|
|
||||||
|
%table.table.table-striped
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th= heading_helper(PrinterJobUpdate, :created_at)
|
||||||
|
%th= heading_helper(PrinterJobUpdate, :state)
|
||||||
|
%th= heading_helper(PrinterJobUpdate, :message)
|
||||||
|
%tbody
|
||||||
|
- @job.printer_job_updates.each do |u|
|
||||||
|
%tr
|
||||||
|
%td= format_time u.created_at
|
||||||
|
%td= u.state
|
||||||
|
%td= u.message
|
11
plugins/printer/config/routes.rb
Normal file
11
plugins/printer/config/routes.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Rails.application.routes.draw do
|
||||||
|
scope '/:foodcoop' do
|
||||||
|
resources :printer, only: [:show] do
|
||||||
|
get :socket, on: :collection
|
||||||
|
end
|
||||||
|
|
||||||
|
resources :printer_jobs, only: [:index, :create, :show, :destroy] do
|
||||||
|
get :document, on: :member
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,20 @@
|
||||||
|
class CreatePrinterJobs < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :printer_jobs do |t|
|
||||||
|
t.references :order
|
||||||
|
t.string :document, null: false
|
||||||
|
t.integer :created_by_user_id, null: false
|
||||||
|
t.integer :finished_by_user_id
|
||||||
|
t.datetime :finished_at, index: true
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table :printer_job_updates do |t|
|
||||||
|
t.references :printer_job, null: false
|
||||||
|
t.datetime :created_at, null: false
|
||||||
|
t.string :state, null: false
|
||||||
|
t.text :message
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :printer_job_updates, [:printer_job_id, :created_at]
|
||||||
|
end
|
||||||
|
end
|
22
plugins/printer/foodsoft_printer.gemspec
Normal file
22
plugins/printer/foodsoft_printer.gemspec
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
$:.push File.expand_path("../lib", __FILE__)
|
||||||
|
|
||||||
|
# Maintain your gem's version:
|
||||||
|
require "foodsoft_printer/version"
|
||||||
|
|
||||||
|
# Describe your gem and declare its dependencies:
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = "foodsoft_printer"
|
||||||
|
s.version = FoodsoftPrinter::VERSION
|
||||||
|
s.authors = ["paroga"]
|
||||||
|
s.email = ["paroga@paroga.com"]
|
||||||
|
s.homepage = "https://github.com/foodcoops/foodsoft"
|
||||||
|
s.summary = "Printer plugin for foodsoft."
|
||||||
|
s.description = "Add a printer queue to foodsoft."
|
||||||
|
|
||||||
|
s.files = Dir["{app,config,db,lib}/**/*"] + ["Rakefile", "README.md"]
|
||||||
|
s.test_files = Dir["test/**/*"]
|
||||||
|
|
||||||
|
s.add_dependency "rails"
|
||||||
|
s.add_dependency "deface", "~> 1.0"
|
||||||
|
s.add_dependency "tubesock"
|
||||||
|
end
|
9
plugins/printer/lib/foodsoft_printer.rb
Normal file
9
plugins/printer/lib/foodsoft_printer.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
require 'foodsoft_printer/engine'
|
||||||
|
require 'foodsoft_printer/order_printer_jobs'
|
||||||
|
require 'tubesock'
|
||||||
|
|
||||||
|
module FoodsoftPrinter
|
||||||
|
def self.enabled?
|
||||||
|
FoodsoftConfig[:use_printer]
|
||||||
|
end
|
||||||
|
end
|
26
plugins/printer/lib/foodsoft_printer/engine.rb
Normal file
26
plugins/printer/lib/foodsoft_printer/engine.rb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
module FoodsoftPrinter
|
||||||
|
class Engine < ::Rails::Engine
|
||||||
|
def navigation(primary, context)
|
||||||
|
return unless FoodsoftPrinter.enabled?
|
||||||
|
unless primary[:orders].nil?
|
||||||
|
sub_nav = primary[:orders].sub_navigation
|
||||||
|
sub_nav.items <<
|
||||||
|
SimpleNavigation::Item.new(primary, :printer_jobs, I18n.t('navigation.orders.printer_jobs'), context.printer_jobs_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_foodsoft_config(cfg)
|
||||||
|
cfg[:use_printer] = false
|
||||||
|
end
|
||||||
|
|
||||||
|
initializer 'foodsoft_printer.order_printer_jobs' do |app|
|
||||||
|
if Rails.configuration.cache_classes
|
||||||
|
OrderPrinterJobs.install
|
||||||
|
else
|
||||||
|
ActionDispatch::Reloader.to_prepare do
|
||||||
|
OrderPrinterJobs.install
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
26
plugins/printer/lib/foodsoft_printer/order_printer_jobs.rb
Normal file
26
plugins/printer/lib/foodsoft_printer/order_printer_jobs.rb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
module FoodsoftPrinter
|
||||||
|
|
||||||
|
module OrderPrinterJobs
|
||||||
|
def self.included(base) # :nodoc:
|
||||||
|
base.class_eval do
|
||||||
|
has_many :printer_jobs, dependent: :destroy
|
||||||
|
|
||||||
|
alias foodsoft_printer_orig_finish! finish!
|
||||||
|
|
||||||
|
def finish!(user)
|
||||||
|
foodsoft_printer_orig_finish!(user)
|
||||||
|
unless finished?
|
||||||
|
printer_jobs.unfinished.each do |job|
|
||||||
|
job.add_update! 'ready'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.install
|
||||||
|
Order.send :include, self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
3
plugins/printer/lib/foodsoft_printer/version.rb
Normal file
3
plugins/printer/lib/foodsoft_printer/version.rb
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module FoodsoftPrinter
|
||||||
|
VERSION = "0.0.1"
|
||||||
|
end
|
Loading…
Reference in a new issue