12 Commits

Author SHA1 Message Date
6365fe6679 2026.3.11 2026-03-19 08:45:32 -04:00
4d6c16373a Don't display form if issue if closed 2026-03-19 08:38:21 -04:00
20b7564c38 Added items to admin menu 2026-03-19 07:13:43 -04:00
9820646857 allow math on unit price field 2026-03-18 22:02:53 -04:00
647ce4c63a 2026.3.10 2026-03-17 23:36:29 -04:00
4367c77393 updaed locale 2026-03-17 20:49:58 -04:00
8c0ce8ce48 updated locale 2026-03-17 20:45:21 -04:00
9fd1f4fb7d removed back link 2026-03-17 20:37:58 -04:00
cca822c570 better display flags 2026-03-17 20:35:40 -04:00
795906b4f5 Sync Item account numbers 2026-03-17 20:26:52 -04:00
d5e59c120c Make qbo item inactive if deleted 2026-03-17 19:45:36 -04:00
e640e61648 Added missing label_item_new 2026-03-17 19:45:14 -04:00
10 changed files with 56 additions and 22 deletions

View File

@@ -18,6 +18,7 @@ class Item < QboBaseModel
self.inheritance_column = :_type_disabled
qbo_sync push: true
after_initialize :set_defaults, if: :new_record?
before_destroy :make_inactive
# Updates Both local & remote DB account ref
def account_id=(id)
@@ -37,6 +38,11 @@ class Item < QboBaseModel
super
end
def make_inactive
details.active = false
push_to_qbo
end
def ref
Quickbooks::Model::BaseReference.new
end

View File

@@ -24,6 +24,9 @@ class ItemSyncService < SyncServiceBase
map_attribute :active, :active?
map_attribute :taxable, :taxable?
map_attribute :account do |remote|
Account.find remote.income_account_ref.value.to_i
end
map_attributes :description, :id, :name, :sku, :type, :unit_price
end

View File

@@ -15,7 +15,10 @@
</p>
<p>
<%= f.number_field :unit_price, step: 0.01, size: 10 %>
<%= f.text_field :unit_price,
class: "price-field",
inputmode: "decimal",
autocomplete: "off"%>
</p>
<p>

View File

@@ -1,5 +1,3 @@
<h2>Edit Item</h2>
<h2><%=t(:label_edit_item)%></h2>
<%= render "form" %>
<%= link_to "Back", items_path %>
<%= render "form" %>

View File

@@ -26,10 +26,18 @@
<td class="description"><%= truncate(item.description, length: 60) %></td>
<td class="unit_price"><%= number_to_currency(item.unit_price) %></td>
<td class="taxable center">
<%= item.taxable ? content_tag(:span, '', class: 'icon icon-ok') : "" %>
<% if item.taxable %>
<span class="icon icon-ok" style="color: green;"><%=t(:yes)%></span>
<% else %>
<span class="icon icon-not-ok" style="color: #999;"><%=t(:no)%></span>
<% end %>
</td>
<td class="active center">
<%= checked_image item.active %>
<% if item.active %>
<span class="icon icon-ok" style="color: green;"><%=t(:yes)%></span>
<% else %>
<span class="icon icon-not-ok" style="color: #999;"><%=t(:no)%></span>
<% end %>
</td>
<td class="buttons">
<%= link_to l(:button_edit), edit_item_path(item), class: 'icon icon-edit' %>

View File

@@ -1,5 +1,5 @@
<h2>New Item</h2>
<h2><%=t(:label_item_new)%></h2>
<%= render "form" %>
<%= link_to "Back", items_path %>
<%= link_to t(:label_back), items_path %>

View File

@@ -8,27 +8,27 @@
<div class="issue details"> <div class="attributes">
<div class="splitcontent">
<div class="splitcontentleft">
<p><strong>SKU:</strong> <%= @item.sku.presence || "-" %></p>
<p><strong>Type:</strong> <%= @item.type.presence || "-" %></p>
<p><strong>Unit Price:</strong> <%= number_to_currency(@item.unit_price) %></p>
<p><strong><%=t(:field_sku)%>:</strong> <%= @item.sku.presence || "-" %></p>
<p><strong><%=t(:label_type)%>:</strong> <%= @item.type.presence || "-" %></p>
<p><strong><%=t(:field_unit_price)%>:</strong> <%= number_to_currency(@item.unit_price) %></p>
</div>
<div class="splitcontentleft">
<p><strong>Account:</strong> <%= @item.account&.name || "-" %></p>
<p><strong><%=t(:label_account)%>:</strong> <%= @item.account&.name || "-" %></p>
<p>
<strong>Taxable:</strong>
<strong><%=t(:field_taxable)%>:</strong>
<% if @item.taxable %>
<span class="icon icon-ok" style="color: green;">Yes</span>
<span class="icon icon-ok" style="color: green;"><%=t(:yes)%></span>
<% else %>
<span class="icon icon-not-ok" style="color: #999;">No</span>
<span class="icon icon-not-ok" style="color: #999;"><%=t(:no)%></span>
<% end %>
</p>
<p>
<strong>Active:</strong>
<strong><%=t(:label_active)%>:</strong>
<% if @item.active %>
<span class="icon icon-ok" style="color: green;">Yes</span>
<span class="icon icon-ok" style="color: green;"><%=t(:yes)%></span>
<% else %>
<span class="icon icon-not-ok" style="color: #999;">No</span>
<span class="icon icon-not-ok" style="color: #999;"><%=t(:no)%></span>
<% end %>
</p>
</div>
@@ -36,7 +36,7 @@
<hr />
<p><strong>Description:</strong></p>
<p><strong><%=t(:label_description)%>:</strong></p>
<div class="wiki" style="padding-left: 20px;">
<%= @item.description.presence || "<em>No description provided</em>".html_safe %>
</div>

View File

@@ -16,21 +16,28 @@ en:
field_taxable: "Taxable"
field_unit_price: "Unit Price"
label_active: "Activie"
label_account: "Account"
label_accounts: "Accounts"
label_back: "Back"
label_account_count: "Number of Accounts:"
label_default_account: "Default Item Income Account"
label_description: "Description"
label_edit_item: "Edit Item"
label_item: "Item"
label_item_count: "Item Count:"
label_items: "Items"
label_line_items: "Line Items"
label_price: "Unit Price"
label_item_new: "New Item"
label_no: "No"
label_qty: "Quantity"
label_remove: "Remove"
label_sync_now_accounts: "Sync Accounts"
label_sync_now_items: "Sync Items"
label_type: "Type"
label_total: "Total"
label_yes: "Yes"
notice_added_from: "Added from issue #"

10
init.rb
View File

@@ -14,7 +14,7 @@ Redmine::Plugin.register :redmine_qbo_lineitems do
name 'Redmine QBO Line Items plugin'
author 'Rick Barrette'
description 'A plugin for Redmine to extend the capabilitys of the Redmine QuickBooks Online plugin to attach billable line items to an isuue'
version '2026.3.9'
version '2026.3.11'
url 'https://github.com/rickbarrette/redmine_qbo_lineitems'
author_url 'https://barrettefabrication.com'
requires_redmine version_or_higher: '6.1.0'
@@ -31,6 +31,14 @@ Redmine::Plugin.register :redmine_qbo_lineitems do
Issue.safe_attributes :line_items_attributes
end
# Administration menu extension
Redmine::MenuManager.map :admin_menu do |menu|
menu.push :redmine_qbo_lineitems, { controller: 'items', action: 'index' },
icon: 'list',
caption: :label_items,
html: { class: 'icon icon-list' }
end
# Dynamically load all Hooks & Patches recursively
base_dir = File.join(File.dirname(__FILE__), 'lib')

View File

@@ -25,6 +25,7 @@ module RedmineQboLineItems
end
def view_issues_edit_notes_bottom(context = {})
return if context[:issue].closed?
context[:controller].send(:render_to_string, {
partial: 'line_items/issue_form',
locals: {