Compare commits

..

26 Commits

Author SHA1 Message Date
Rachel Arnould
b5020cc740 Update en.yml
Removing mentions about UFC Que choisir and changing the link
2024-11-25 13:31:36 +01:00
filipefurtad0
7a2a6fab21 Update all locales with the latest Transifex translations 2024-11-22 16:36:15 -06:00
Filipe
0f4ca50d0e Merge pull request #12962 from bouaik/Scroll-to-top-when-using-pagination
Scroll to top when using pagination
2024-11-22 15:13:38 -06:00
Maikel
3ec8cd24d3 Merge pull request #12960 from mkllnk/dfc-link-backorder
Store link to open backorder
2024-11-22 10:22:43 +11:00
Maikel
d0dcc92ca7 Merge pull request #12976 from dacook/update-docker-readme
Update docker readme
2024-11-21 13:25:42 +11:00
Filipe
22f3afc7f7 Merge pull request #12930 from chahmedejaz/task/12878-add-variant-name-in-od-report
Report Orders and Distributors should display variant
2024-11-20 12:23:36 -06:00
Maikel Linke
46048dcd18 Handle empty backorder
Backorder can become empty after a customer cancels their order. Then we
don't want to fail but also don't need to place an order.
2024-11-19 15:53:59 +11:00
Maikel Linke
a8fb6492f4 Lookup backorder for updates with saved link 2024-11-19 15:53:59 +11:00
Maikel Linke
4610141ed8 Add shortcut to order's exchange 2024-11-19 15:53:59 +11:00
Maikel Linke
8098131dba Store link to open backorder
We don't use the link yet, but it's there.
2024-11-19 15:53:59 +11:00
Maikel Linke
597d9ad314 Add semantic links to Exchange 2024-11-19 15:53:59 +11:00
Maikel Linke
1ce0b25bb0 Switch SemanticLink to use new association
And ActiveRecord magic does the rest when used correctly.
2024-11-19 15:53:58 +11:00
Maikel Linke
c07ec6cdfd Polymorphically associate SemanticLinks to variant 2024-11-19 15:53:58 +11:00
Maikel Linke
48e8ad3dd0 Add subject column to semantic links 2024-11-19 15:53:58 +11:00
David Cook
60d4cd60ff Merge pull request #12972 from duleorlovic/12971_add_serbian_lang
Add Serbian lang: tx pull -l sr fix #12971
2024-11-19 09:46:59 +11:00
Ahmed Ejaz
d62d3041b4 12878: add relations in model 2024-11-18 11:45:02 +05:00
Ahmed Ejaz
42fc0f7230 12878: add model in migration 2024-11-18 11:13:02 +05:00
Ahmed Ejaz
39fa8e0ace 12878: fix migration class name 2024-11-14 11:04:28 +05:00
David Cook
d9809fc1f4 Update docker readme
I think it was misleading, and considering the challenges everyone seems to have, we need to be more realistic.

[skip ci]
2024-11-14 09:56:59 +11:00
Dusan Orlovic
528c851e89 Add Serbian lang: tx pull -l sr fix #12971 2024-11-10 20:43:13 +01:00
bouaik
8709c137c7 Scroll to top when using pagination 2024-11-01 11:39:41 +01:00
Ahmed Ejaz
355541e8de Update db/migrate/20241011071014_update_item_name_to_product_in_od_report.rb
Co-authored-by: David Cook <david@redcliffs.net>
2024-10-18 12:32:14 +05:00
Ahmed Ejaz
e10c3dc59b add specs for migration 2024-10-18 12:31:05 +05:00
Ahmed Ejaz
641b7beee3 add specs 2024-10-12 04:47:45 +05:00
Ahmed Ejaz
60e8db9adc 12878: add migration to show new column product column 2024-10-12 03:52:00 +05:00
Ahmed Ejaz
b7285e48b3 12878: update unit to full_name to display in variant column 2024-10-12 03:50:11 +05:00
34 changed files with 5458 additions and 185 deletions

View File

@@ -28,7 +28,7 @@ class AmendBackorderJob < ApplicationJob
urls = FdcUrlBuilder.new(reference_link)
orderer = FdcBackorderer.new(user, urls)
backorder = orderer.find_open_order
backorder = orderer.find_open_order(order)
variants = order_cycle.variants_distributed_by(distributor)
adjust_quantities(order_cycle, user, backorder, urls, variants)

View File

@@ -13,7 +13,7 @@ class BackorderJob < ApplicationJob
sidekiq_options retry: 0
def self.check_stock(order)
links = SemanticLink.where(variant_id: order.line_items.select(:variant_id))
links = SemanticLink.where(subject: order.variants)
perform_later(order) if links.exists?
rescue StandardError => e
@@ -133,5 +133,7 @@ class BackorderJob < ApplicationJob
.perform_later(
user, order.distributor, order.order_cycle, placed_order.semanticId
)
order.exchange.semantic_links.create!(semantic_id: placed_order.semanticId)
end
end

View File

@@ -19,12 +19,18 @@ class CompleteBackorderJob < ApplicationJob
# someone else's order.
def perform(user, distributor, order_cycle, order_id)
order = FdcBackorderer.new(user, nil).find_order(order_id)
return if order&.lines.blank?
urls = FdcUrlBuilder.new(order.lines[0].offer.offeredItem.semanticId)
variants = order_cycle.variants_distributed_by(distributor)
adjust_quantities(order_cycle, user, order, urls, variants)
FdcBackorderer.new(user, urls).complete_order(order)
exchange = order_cycle.exchanges.outgoing.find_by(receiver: distributor)
exchange.semantic_links.find_by(semantic_id: order_id)&.destroy!
rescue StandardError
BackorderMailer.backorder_incomplete(user, distributor, order_cycle, order_id).deliver_later

View File

@@ -36,7 +36,7 @@ class StockSyncJob < ApplicationJob
def self.catalog_ids(order)
stock_controlled_variants = order.variants.reject(&:on_demand)
links = SemanticLink.where(variant_id: stock_controlled_variants.map(&:id))
links = SemanticLink.where(subject: stock_controlled_variants)
semantic_ids = links.pluck(:semantic_id)
semantic_ids.map do |product_id|
FdcUrlBuilder.new(product_id).catalog_url

View File

@@ -22,6 +22,10 @@ class Exchange < ApplicationRecord
has_many :exchange_fees, dependent: :destroy
has_many :enterprise_fees, through: :exchange_fees
# Links to open backorders of a distributor (outgoing exchanges only)
# Don't allow removal of distributor from OC while we have an open backorder.
has_many :semantic_links, as: :subject, dependent: :restrict_with_error
validates :sender_id, uniqueness: { scope: [:order_cycle_id, :receiver_id, :incoming] }
before_destroy :delete_related_exchange_variants, prepend: true

View File

@@ -2,7 +2,9 @@
# Link a Spree::Variant to an external DFC SuppliedProduct.
class SemanticLink < ApplicationRecord
belongs_to :variant, class_name: "Spree::Variant"
self.ignored_columns += [:variant_id]
belongs_to :subject, polymorphic: true
validates :semantic_id, presence: true
end

View File

@@ -67,8 +67,12 @@ module Spree
class_name: 'Spree::Adjustment',
dependent: :destroy
has_many :invoices, dependent: :restrict_with_exception
belongs_to :order_cycle, optional: true
has_one :exchange, ->(order) {
outgoing.to_enterprise(order.distributor)
}, through: :order_cycle, source: :exchanges
has_many :semantic_links, through: :exchange
belongs_to :distributor, class_name: 'Enterprise', optional: true
belongs_to :customer, optional: true
has_one :proxy_order, dependent: :destroy

View File

@@ -60,7 +60,7 @@ module Spree
has_many :exchanges, through: :exchange_variants
has_many :variant_overrides, dependent: :destroy
has_many :inventory_items, dependent: :destroy
has_many :semantic_links, dependent: :delete_all
has_many :semantic_links, as: :subject, dependent: :delete_all
has_many :supplier_properties, through: :supplier, source: :properties
localize_number :price, :weight

View File

@@ -10,7 +10,7 @@ class FdcBackorderer
end
def find_or_build_order(ofn_order)
find_open_order || build_new_order(ofn_order)
find_open_order(ofn_order) || build_new_order(ofn_order)
end
def build_new_order(ofn_order)
@@ -19,7 +19,37 @@ class FdcBackorderer
end
end
def find_open_order
# Try the new method and fall back to old method.
def find_open_order(ofn_order)
lookup_open_order(ofn_order) || find_last_open_order
end
def lookup_open_order(ofn_order)
# There should be only one link at the moment but we may support
# ordering from multiple suppliers one day.
semantic_ids = ofn_order.semantic_links.pluck(:semantic_id)
semantic_ids.lazy
# Make sure we select an order from the right supplier:
.select { |id| id.starts_with?(urls.orders_url) }
# Fetch the order from the remote DFC server, lazily:
.map { |id| find_order(id) }
.compact
# Just in case someone completed the order without updating our database:
.select { |o| o.orderStatus[:path] == "Held" }
.first
# The DFC Connector doesn't recognise status values properly yet.
# So we are overriding the value with something that can be exported.
&.tap { |o| o.orderStatus = "dfc-v:Held" }
end
# DEPRECATED
#
# We now store links to orders we placed. So we don't need to search
# through all orders and pick a random open one.
# But for compatibility with currently open order cycles that don't have
# a stored link yet, we keep this method as well.
def find_last_open_order
graph = import(urls.orders_url)
open_orders = graph&.select do |o|
o.semanticType == "dfc-b:Order" && o.orderStatus[:path] == "Held"

View File

@@ -9,6 +9,8 @@ export default class extends Controller {
}
changePage(event) {
const productsForm = document.querySelector("#products-form");
productsForm.scrollIntoView({ behavior: "smooth" });
this.page.value = event.target.dataset.page;
this.submitSearch();
this.page.value = 1;

View File

@@ -1412,7 +1412,7 @@ en:
connected_apps:
legend: "Connected apps"
affiliate_sales_data:
title: "INRAE / UFC QUE CHOISIR Research"
title: "INRAE Research"
tagline: "Allow this research project to access your orders data anonymously"
enable: "Allow data sharing"
disable: "Stop sharing"
@@ -1420,10 +1420,10 @@ en:
need_to_be_manager: "Only managers can connect apps."
description_html: |
<p>
INRAE and UFC QUE CHOISIR are teaming up to study food prices in short food systems and compare them with prices in the supermarket, for a given set of products. The data that is used by INRAE is mixed with data coming from other short food chain platforms in France. No individual product prices will be publicly disclosed through this project.
INRAE are studiying food prices in short food systems and compare them with prices in the supermarket, for a given set of products. The data that is used by INRAE is mixed with data coming from other short food chain platforms in France. No individual product prices will be publicly disclosed through this project.
</p>
<p>
<a href="https://apropos.coopcircuits.fr/"
<a href="https://pepr-sams.fr/2024/03/12/plat4terfood/"
target="_blank"><b>Learn more about this research project</b>
<i class="icon-external-link"></i></a>
</p>

View File

@@ -1695,6 +1695,7 @@ en_FR:
pack_by_customer: Pack By Customer
pack_by_supplier: Pack By Supplier
pack_by_product: Pack By Product
pay_your_suppliers: Pay your suppliers
display:
report_is_big: "This report is big and may slow down your device."
display_anyway: "Display anyway"
@@ -1740,6 +1741,8 @@ en_FR:
enterprise_fee_summary:
name: "Enterprise Fee Summary"
description: "Summary of Enterprise Fees collected"
suppliers:
name: Suppliers
enterprise_fees_with_tax_report_by_order: "Enterprise Fees With Tax Report By Order"
enterprise_fees_with_tax_report_by_producer: "Enterprise Fees With Tax Report By Producer"
errors:
@@ -3026,6 +3029,8 @@ en_FR:
report_render_options: Rendering Options
report_header_ofn_uid: OFN UID
report_header_order_cycle: Order Cycle
report_header_order_cycle_start_date: OC Start Date
report_header_order_cycle_end_date: OC End Date
report_header_user: User
report_header_email: Email
report_header_status: Status
@@ -3046,6 +3051,7 @@ en_FR:
report_header_hub_legal_name: "Hub Legal Name"
report_header_hub_contact_name: "Hub Contact Name"
report_header_hub_email: "Hub Public Email"
report_header_hub_contact_email: Hub Contact Email
report_header_hub_owner_email: Hub Owner Email
report_header_hub_phone: "Hub Phone Number"
report_header_hub_address_line1: "Hub Address Line 1"
@@ -3118,6 +3124,8 @@ en_FR:
report_header_producer_suburb: Producer Suburb
report_header_producer_tax_status: Producer Tax Status
report_header_producer_charges_sales_tax?: GST/VAT Registered
report_header_producer_abn_acn: Producer ABN/ACN
report_header_producer_address: Producer Address
report_header_unit: Unit
report_header_group_buy_unit_quantity: Group Buy Unit Quantity
report_header_cost: Cost
@@ -3178,7 +3186,11 @@ en_FR:
report_header_total_units: Total Units
report_header_sum_max_total: "Sum Max Total"
report_header_total_excl_vat: "Total excl. tax (%{currency_symbol})"
report_header_total_fees_excl_tax: "Total fees excl. tax (%{currency_symbol})"
report_header_total_tax_on_fees: "Total tax on fees (%{currency_symbol})"
report_header_total: "Total (%{currency_symbol})"
report_header_total_incl_vat: "Total incl. tax (%{currency_symbol})"
report_header_total_excl_fees_and_tax: "Total excl. fees and tax (%{currency_symbol})"
report_header_temp_controlled: TempControlled?
report_header_is_producer: Producer?
report_header_not_confirmed: Not Confirmed

View File

@@ -53,6 +53,7 @@ en_GB:
primary_taxon: "Product Category"
shipping_category_id: "Shipping Category"
supplier: "Supplier"
variant_unit: "Unit Scale"
variant_unit_name: "Variant Unit Name"
unit_value: "Unit value"
spree/credit_card:
@@ -80,6 +81,8 @@ en_GB:
white_label_logo_link: "Link for the logo used in shopfront"
errors:
models:
enterprise_fee:
inherit_tax_requires_per_item_calculator: "Inheriting the tax category requires a per-item calculator."
spree/image:
attributes:
attachment:
@@ -104,6 +107,9 @@ en_GB:
count_on_hand:
using_producer_stock_settings_but_count_on_hand_set: "must be blank because using producer stock settings"
limited_stock_but_no_count_on_hand: "must be specified because forcing limited stock"
connected_apps:
vine:
api_request_error: "An error occured when connecting to Vine API"
messages:
confirmation: "doesn't match %{attribute}"
blank: "can't be blank"
@@ -315,7 +321,34 @@ en_GB:
We will look into it but please let us know if the problem persists.
backorder_mailer:
backorder_failed:
subject: "An automatic backorder failed"
headline: "Backordering failed"
description: |
We tried to place or update a backorder for out-of-stock items but
something went wrong. You may have negative stock and need to resolve
the issue to order more stock in.
hints: |
You may need to go to the OIDC settings and reconnect your account.
Also check that your supplier's catalog hasn't changed and is still
supplying all products you need. And please get in touch with us if
you have any questions.
order: "Affected order: %{number}"
stock: "Stock "
product: "Product"
backorder_incomplete:
subject: "An automatic backorder failed to complete"
headline: "Your backorder is still a draft"
description: |
We tried to complete a backorder for out-of-stock items but
something went wrong. The backorder quantities may be too high if
you had cancellations. And your backorder won't be fulfilled while
it's in draft state.
hints: |
You may need to go to the OIDC settings and reconnect your account.
Also check that your supplier's catalog hasn't changed and is still
supplying all products you need. And please get in touch with us if
you have any questions.
affected: "%{enterprise}: %{order_cycle}"
enterprise_mailer:
confirmation_instructions:
subject: "Please confirm the email address for %{enterprise}"
@@ -554,10 +587,13 @@ en_GB:
clone: Clone
delete: Delete
remove: Remove
preview: Preview
image:
edit: Edit
product_preview:
product_preview: Product preview
shop_tab: Shop
product_details_tab: Product details
adjustments:
skipped_changing_canceled_order: "You can't change a cancelled order."
begins_at: Begins At
@@ -684,6 +720,7 @@ en_GB:
connected_apps_enabled:
discover_regen: Discover Regenerative portal
affiliate_sales_data: DFC anonymised orders API for research purposes
vine: Voucher Integration Engine (VINE)
update:
resource: Connected app settings
customers:
@@ -783,6 +820,7 @@ en_GB:
variants:
infinity: "Infinity"
to_order_tip: "Items made to order do not have a set stock level, such as loaves of bread made fresh to order."
back_to_products_list: "Back To Products List"
editing_product: "Editing Product"
tabs:
product_details: "Product Details"
@@ -1303,11 +1341,21 @@ en_GB:
connected_apps:
legend: "Connected apps"
affiliate_sales_data:
title: "INRAE / UFC QUE CHOISIR Research"
tagline: "Allow this research project to access your orders data anonymously"
enable: "Allow data sharing"
disable: "Stop sharing"
loading: "Loading"
need_to_be_manager: "Only managers can connect apps."
description_html: |
<p>
INRAE and UFC QUE CHOISIR are teaming up to study food prices in short food systems and compare them with prices in the supermarket, for a given set of products. The data that is used by INRAE is mixed with data coming from other short food chain platforms in France. No individual product prices will be publicly disclosed through this project.
</p>
<p>
<a href="https://apropos.coopcircuits.fr/"
target="_blank"><b>Learn more about this research project</b>
<i class="icon-external-link"></i></a>
</p>
discover_regen:
title: "Discover Regenerative"
tagline: "Allow Discover Regenerative to publish your enterprise information."
@@ -1332,9 +1380,25 @@ en_GB:
<i class="icon-external-link"></i></a>
</p>
vine:
title: "Voucher Integration Engine (VINE)"
tagline: "Allow redemption of VINE vouchers in your shopfront."
enable: "Resources"
disable: "Disconnect"
need_to_be_manager: "Only managers can connect apps."
vine_api_key: "VINE API Key"
vine_secret: "VINE secret"
description_html: |
<p>
To enable VINE for your enterprise, enter your API key and secret.
</p>
<p>
<a href="#" target="_blank"><b>VINE</b>
<i class="icon-external-link"></i></a>
</p>
api_parameters_empty: "Please enter an API key and a secret"
api_parameters_error: "Check you entered your API key and secret correctly, contact your instance manager if the error persists"
connection_error: "API connection error, please try again"
setup_error: "VINE API is not configured, please contact your instance manager"
actions:
edit_profile: Settings
properties: Properties
@@ -1387,8 +1451,10 @@ en_GB:
contact_name: Contact Name
edit:
editing: 'Settings:'
back_link: Back To Enterprises List
new:
title: New Enterprise
back_link: Back To Enterprises List
welcome:
welcome_title: Welcome to the Open Food Network!
welcome_text: You have successfully created a
@@ -1629,6 +1695,7 @@ en_GB:
pack_by_customer: Pack By Customer
pack_by_supplier: Pack By Supplier
pack_by_product: Pack By Product
pay_your_suppliers: Pay your suppliers
display:
report_is_big: "This report is big and may slow down your device."
display_anyway: "Display anyway"
@@ -1674,6 +1741,8 @@ en_GB:
enterprise_fee_summary:
name: "Enterprise Fee Summary"
description: "Summary of Enterprise Fees collected"
suppliers:
name: Suppliers
enterprise_fees_with_tax_report_by_order: "Enterprise Fees With Tax Report By Order"
enterprise_fees_with_tax_report_by_producer: "Enterprise Fees With Tax Report By Producer"
errors:
@@ -2960,6 +3029,8 @@ en_GB:
report_render_options: Rendering Options
report_header_ofn_uid: OFN UID
report_header_order_cycle: Order Cycle
report_header_order_cycle_start_date: OC Start Date
report_header_order_cycle_end_date: OC End Date
report_header_user: User
report_header_email: Email
report_header_status: Status
@@ -2980,6 +3051,7 @@ en_GB:
report_header_hub_legal_name: "Hub Legal Name"
report_header_hub_contact_name: "Hub Contact Name"
report_header_hub_email: "Hub Public Email"
report_header_hub_contact_email: Hub Contact Email
report_header_hub_owner_email: Hub Owner Email
report_header_hub_phone: "Hub Phone Number"
report_header_hub_address_line1: "Hub Address Line 1"
@@ -3052,6 +3124,8 @@ en_GB:
report_header_producer_suburb: Producer Suburb
report_header_producer_tax_status: Tax Rate Name
report_header_producer_charges_sales_tax?: GST/VAT Registered
report_header_producer_abn_acn: Producer ABN/ACN
report_header_producer_address: Producer Address
report_header_unit: Unit
report_header_group_buy_unit_quantity: Group Buy Unit Quantity
report_header_cost: Cost
@@ -3112,7 +3186,11 @@ en_GB:
report_header_total_units: Total Units
report_header_sum_max_total: "Sum Max Total"
report_header_total_excl_vat: "Total excl. tax (%{currency_symbol})"
report_header_total_fees_excl_tax: "Total fees excl. tax (%{currency_symbol})"
report_header_total_tax_on_fees: "Total tax on fees (%{currency_symbol})"
report_header_total: "Total (%{currency_symbol})"
report_header_total_incl_vat: "Total incl. tax (%{currency_symbol})"
report_header_total_excl_fees_and_tax: "Total excl. fees and tax (%{currency_symbol})"
report_header_temp_controlled: TempControlled?
report_header_is_producer: Producer?
report_header_not_confirmed: Not Confirmed
@@ -3589,7 +3667,22 @@ en_GB:
Please refresh the page and try again, if it fails a second time,
please contact us for support.
trix:
bold: "Bold"
bullets: "Bullets"
code: "Code"
heading1: "Heading"
hr: "Horizontal rule"
indent: "Increase Level"
italic: "Italic"
link: "Link"
numbers: "Numbers"
outdent: "Decrease Level"
quote: "Quote"
redo: "Redo"
strike: "Strikethrough"
undo: "Undo"
unlink: "Unlink"
url: "URL"
urlPlaceholder: "Please enter a URL to insert"
inflections:
each:
@@ -3902,6 +3995,7 @@ en_GB:
tax_rate_amount_explanation: "Tax rates are a decimal amount to aid in calculations, (i.e. if the tax rate is 5% then enter 0.05)"
included_in_price: "Included in Price"
show_rate_in_label: "Show rate in label"
back_to_tax_rates_list: "Back To Tax Rates List"
tax_settings: "Tax Settings"
zones: "Zones"
new_zone: "New Zone"
@@ -3914,6 +4008,7 @@ en_GB:
iso_name: "ISO Name"
states_required: "Counties Required"
editing_country: "Editing Country"
back_to_countries_list: "Back To Countries List"
states: "Counties"
abbreviation: "Abbreviation"
new_state: "New County"
@@ -4078,6 +4173,7 @@ en_GB:
continue: "Continue"
new:
new_return_authorization: "New Return Authorisation"
back_to_return_authorizations_list: "Back To Return Authorisations List"
continue: "Continue"
edit:
receive: "receive"
@@ -4290,6 +4386,8 @@ en_GB:
new_product: "New Product"
supplier: "Supplier"
supplier_select_placeholder: "Select a supplier"
search_for_suppliers: "Search for suppliers"
search_for_units: "Search for units"
product_name: "Product Name"
units: "Unit Size"
value: "Value"
@@ -4406,6 +4504,7 @@ en_GB:
total: "Total"
billing_address_name: "Name"
taxons:
back_to_list: "Back To Product Categories List"
index:
title: "Product Categories"
new_taxon: 'New product category'
@@ -4416,6 +4515,7 @@ en_GB:
destroy:
delete_taxon:
success: "Successfully deleted the product category"
error: "Unable to delete the product category due to assigned products."
form:
name: Name
meta_title: Meta Title

View File

@@ -1697,6 +1697,7 @@ fr:
pack_by_customer: Préparation des commandes par Acheteur
pack_by_supplier: Préparation des commandes par Producteur
pack_by_product: Préparation des commandes par Produit
pay_your_suppliers: Payer vos fournisseurs
display:
report_is_big: "Ce rapport est volumineux et risque de ralentir l'appareil sur lequel vous êtes en train de le consulter."
display_anyway: "Afficher quand même"
@@ -1744,6 +1745,8 @@ fr:
enterprise_fee_summary:
name: "Résumé des marges et commissions"
description: "Résumé des marges et commissions collectées"
suppliers:
name: Fournisseurs
enterprise_fees_with_tax_report_by_order: "Détail des montants de taxe par commande"
enterprise_fees_with_tax_report_by_producer: "Détail des montants de taxe par producteur"
errors:
@@ -3032,6 +3035,8 @@ fr:
report_render_options: Mise en forme
report_header_ofn_uid: ID OFN
report_header_order_cycle: Cycle de Vente
report_header_order_cycle_start_date: Date d'ouverture du cycle de vente
report_header_order_cycle_end_date: Date de fermeture du cycle de vente
report_header_user: Utilisateur
report_header_email: Email
report_header_status: Statut
@@ -3052,6 +3057,7 @@ fr:
report_header_hub_legal_name: "Raison sociale"
report_header_hub_contact_name: "Nom du contact"
report_header_hub_email: "Email public"
report_header_hub_contact_email: e-mail de contact de la boutique multi-producteurs
report_header_hub_owner_email: Email gestionnaire principal
report_header_hub_phone: "Numéro de téléphone"
report_header_hub_address_line1: "Adresse ligne 1"
@@ -3124,6 +3130,8 @@ fr:
report_header_producer_suburb: Ville Producteur
report_header_producer_tax_status: Soumis à la TVA
report_header_producer_charges_sales_tax?: Soumis à la TVA
report_header_producer_abn_acn: Numéro de SIRET/SIREN du producteur
report_header_producer_address: Adresse du producteur
report_header_unit: Unité
report_header_group_buy_unit_quantity: Nb d'unités achetées (vente par lots)
report_header_cost: Coût
@@ -3184,7 +3192,11 @@ fr:
report_header_total_units: Vol. total
report_header_sum_max_total: "Somme Max Total"
report_header_total_excl_vat: "Total HT (%{currency_symbol})"
report_header_total_fees_excl_tax: "Total commission boutique hors taxe (%{currency_symbol})"
report_header_total_tax_on_fees: "Total taxe sur la commission boutique (%{currency_symbol})"
report_header_total: "Total (%{currency_symbol})"
report_header_total_incl_vat: "Total TTC (%{currency_symbol})"
report_header_total_excl_fees_and_tax: "Total hors commission boutique et taxe (%{currency_symbol})"
report_header_temp_controlled: Temp Contrôlée ?
report_header_is_producer: Producteur ?
report_header_not_confirmed: Non confirmé

4848
config/locales/sr.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,46 @@
class UpdateItemNameToProductInOdReport < ActiveRecord::Migration[7.0]
class ReportRenderingOptions < ActiveRecord::Base
self.belongs_to_required_by_default = false
belongs_to :user, class_name: "Spree::User"
serialize :options, Hash, coder: YAML
end
# OD: Orders and Distributors
def up
# adding subtype filter just to be safe
options = ReportRenderingOptions.where(report_type: 'orders_and_distributors', report_subtype: nil)
options.find_each do |option|
begin
fields_to_show = option.options[:fields_to_show]
next if fields_to_show&.exclude?('item_name')
fields_to_show.delete('item_name')
fields_to_show << 'product'
option.save
rescue StandardError => e
puts "Failed to update rendering option with id: #{option.id}"
puts "Error: #{e.message}"
end
end
end
def down
options = ReportRenderingOptions.where(report_type: 'orders_and_distributors', report_subtype: nil)
options.find_each do |option|
begin
fields_to_show = option.options[:fields_to_show]
next if fields_to_show&.exclude?('product')
fields_to_show.delete('product')
fields_to_show << 'item_name'
option.update(options: option.options)
rescue StandardError => e
puts "Failed to revert rendering option with id: #{option.id}"
puts "Error: #{e.message}"
end
end
end
end

View File

@@ -0,0 +1,14 @@
# frozen_string_literal: true
# rails g migration AddSubjectToSemanticLinks subject:references{polymorphic}
#
# We want to add links to Exchanges as well as Variants.
# The word subject comes from the triple structure of the Semantic Web:
#
# Subject predicate object (variant has linke URL)
class AddSubjectToSemanticLinks < ActiveRecord::Migration[7.0]
def change
# We allow `null` until we filled the new columns with existing data.
add_reference :semantic_links, :subject, polymorphic: true, null: true
end
end

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
class CopySubjectOnSemanticLinks < ActiveRecord::Migration[7.0]
def up
execute <<~SQL.squish
UPDATE semantic_links SET
subject_id = variant_id,
subject_type = 'Spree::Variant'
SQL
end
end

View File

@@ -0,0 +1,8 @@
class ChangeNullOnSemanticLinks < ActiveRecord::Migration[7.0]
def change
change_column_null :semantic_links, :subject_id, false
change_column_null :semantic_links, :subject_type, false
change_column_null :semantic_links, :variant_id, true
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2024_10_23_054951) do
ActiveRecord::Schema[7.0].define(version: 2024_10_30_033956) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
enable_extension "plpgsql"
@@ -401,10 +401,13 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_23_054951) do
end
create_table "semantic_links", force: :cascade do |t|
t.bigint "variant_id", null: false
t.bigint "variant_id"
t.string "semantic_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "subject_type", null: false
t.bigint "subject_id", null: false
t.index ["subject_type", "subject_id"], name: "index_semantic_links_on_subject"
t.index ["variant_id"], name: "index_semantic_links_on_variant_id"
end

View File

@@ -1,9 +1,6 @@
# Docker Scripts
## What's the point?
* Setting up the Open Food Network app on your local machine is quick and easy with the aid of Docker.
* Docker provides a common virtual environment available to all developers and resolves the infamous "but it works on my machine" problem.
* Use the scripts in this directory to execute tasks in Docker. Please note that these scripts are intended to be executed from this app's root directory (/openfoodnetwork). These scripts allow you to bypass the need to keep typing "docker compose run web".
Docker is intended to provide a common virtual environment available to all developers. Please note that it is not commonly used by developers at this time.
## Limitations
1. The docker environment can't directly control your host system browser, which means that browser specs (under `/spec/system/`) and email previews will not work. You may be able to find a solution with [this article](https://evilmartians.com/chronicles/system-of-a-test-setting-up-end-to-end-rails-testing). If so, please contribute!
@@ -120,6 +117,8 @@ You may also need to comment out stuff related to Chromedriver and Chrome. Chrom
See [#8421](https://github.com/openfoodfoundation/openfoodnetwork/issues/8421) for more info
## Script Summary
Use the scripts in this directory to execute tasks in Docker. Please note that these scripts are intended to be executed from this app's root directory (/openfoodnetwork). These scripts allow you to bypass the need to keep typing "docker compose run web".
* docker/build(.ps1): This script builds the Docker containers specified for this app, seeds the database, and logs the screen output for these operations. After you use "git clone" to download this repository, run the docker/build script to start the setup process.
* docker/server(.ps1): Use this script to run this app in the Rails server. This script executes the "docker compose up" command and logs the results. If all goes well, you will be able to view this app on your local browser at http://localhost:3000/.
* docker/test(.ps1): Use this script to run the entire test suite. **Note limitation with system specs mentioned above**.

View File

@@ -14,8 +14,8 @@ module Reporting
customer_phone: proc { |line_item| line_item.order.bill_address.phone },
customer_city: proc { |line_item| line_item.order.bill_address.city },
sku: proc { |line_item| line_item.product.sku },
item_name: proc { |line_item| line_item.product.name },
variant: proc { |line_item| line_item.unit_to_display },
product: proc { |line_item| line_item.product.name },
variant: proc { |line_item| line_item.full_name },
quantity: proc { |line_item| line_item.quantity },
max_quantity: proc { |line_item| line_item.max_quantity },
cost: proc { |line_item| line_item.price * line_item.quantity },

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
FactoryBot.define do
factory :orders_and_distributors_options, class: ReportRenderingOptions do
report_type { "orders_and_distributors" }
end
end

File diff suppressed because one or more lines are too long

View File

@@ -116,13 +116,19 @@ RSpec.describe BackorderJob do
describe "#place_order" do
it "schedules backorder completion for specific enterprises" do
order.order_cycle = build(
order.order_cycle = create(
:simple_order_cycle,
id: 1,
orders_close_at: Date.tomorrow.noon,
)
completion_time = Date.tomorrow.noon + 4.hours
exchange = order.order_cycle.exchanges.create!(
incoming: false,
sender: order.order_cycle.coordinator,
receiver: order.distributor,
)
urls = FdcUrlBuilder.new(product_link)
orderer = FdcBackorderer.new(user, urls)
backorder = orderer.build_new_order(order)
@@ -132,6 +138,7 @@ RSpec.describe BackorderJob do
expect {
subject.place_order(user, order, orderer, backorder)
}.to enqueue_job(CompleteBackorderJob).at(completion_time)
.and change { exchange.semantic_links.count }.by(1)
end
end
end

View File

@@ -25,11 +25,14 @@ RSpec.describe CompleteBackorderJob do
chia_line = orderer.find_or_build_order_line(backorder, chia_offer)
chia_line.quantity = 5
orderer.send_order(backorder)
orderer.send_order(backorder).tap do |o|
exchange.semantic_links.create!(semantic_id: o.semanticId)
end
}
let(:ofn_order) { create(:completed_order_with_totals) }
let(:distributor) { ofn_order.distributor }
let(:order_cycle) { ofn_order.order_cycle }
let(:exchange) { order_cycle.exchanges.outgoing.first }
let(:beans) { ofn_order.line_items[0].variant }
let(:chia) { chia_item.variant }
let(:chia_item) { ofn_order.line_items[1] }
@@ -77,6 +80,9 @@ RSpec.describe CompleteBackorderJob do
.and change {
current_order.lines[1].quantity.to_i
}.from(5).to(7)
.and change {
exchange.semantic_links.count
}.by(-1)
end
it "removes line items", vcr: true do
@@ -109,5 +115,21 @@ RSpec.describe CompleteBackorderJob do
}.to enqueue_mail(BackorderMailer, :backorder_incomplete)
.and raise_error VCR::Errors::UnhandledHTTPRequestError
end
it "skips empty backorders" do
user = nil
distributor = nil
order_cycle = nil
order_id = nil
backorder = DataFoodConsortium::Connector::Order.new(
order_id, orderStatus: "dfc-v:Held"
)
expect_any_instance_of(FdcBackorderer)
.to receive(:find_order).and_return(backorder)
expect {
subject.perform(user, distributor, order_cycle, order_id)
}.not_to raise_error
end
end
end

View File

@@ -11,7 +11,7 @@ RSpec.describe Reporting::Reports::OrdersAndDistributors::Base do
[
'Order date', 'Order Id',
'Customer Name', 'Customer Email', 'Customer Phone', 'Customer City',
'SKU', 'Item name', 'Variant', 'Quantity', 'Max Quantity', 'Cost', 'Shipping Cost',
'SKU', 'Product', 'Variant', 'Quantity', 'Max Quantity', 'Cost', 'Shipping Cost',
'Payment Method',
'Distributor', 'Distributor address', 'Distributor city', 'Distributor postcode',
'Shipping Method', 'Shipping instructions'
@@ -37,7 +37,7 @@ RSpec.describe Reporting::Reports::OrdersAndDistributors::Base do
}
let(:payment_method) { create(:payment_method, distributors: [distributor]) }
let(:payment) { create(:payment, payment_method:, order:) }
let(:line_item) { create(:line_item_with_shipment, product:, order:) }
let(:line_item) { create(:line_item_with_shipment, variant:, order:) }
subject { described_class.new user }
before do
@@ -46,33 +46,35 @@ RSpec.describe Reporting::Reports::OrdersAndDistributors::Base do
order.line_items << line_item
end
it 'should denormalise order and distributor details for display as csv' do
allow(subject).to receive(:unformatted_render?).and_return(true)
table = subject.table_rows
context "without variant name" do
it 'should denormalise order and distributor details for display as csv' do
allow(subject).to receive(:unformatted_render?).and_return(true)
table = subject.table_rows
expect(table.size).to eq 1
expect(table[0]).to eq([
order.reload.completed_at.strftime("%F %T"),
order.id,
bill_address.full_name,
order.email,
bill_address.phone,
bill_address.city,
line_item.product.sku,
line_item.product.name,
line_item.unit_to_display,
line_item.quantity,
line_item.max_quantity,
line_item.price * line_item.quantity,
line_item.distribution_fee,
payment_method.name,
distributor.name,
distributor.address.address1,
distributor.address.city,
distributor.address.zipcode,
shipping_method.name,
shipping_instructions
])
expect(table.size).to eq 1
expect(table[0]).to eq([
order.reload.completed_at.strftime("%F %T"),
order.id,
bill_address.full_name,
order.email,
bill_address.phone,
bill_address.city,
line_item.product.sku,
line_item.product.name,
"1g",
line_item.quantity,
line_item.max_quantity,
line_item.price * line_item.quantity,
line_item.distribution_fee,
payment_method.name,
distributor.name,
distributor.address.address1,
distributor.address.city,
distributor.address.zipcode,
shipping_method.name,
shipping_instructions
])
end
end
it "prints one row per line item" do
@@ -149,6 +151,17 @@ RSpec.describe Reporting::Reports::OrdersAndDistributors::Base do
"Spree::ShippingMethod Load",
]
end
context "with variant name present" do
before do
variant.update_columns(display_name: 'Variant Name');
end
let(:row) { subject.table_rows.first }
it "should display variant name with unit" do
expect(row).to include("Variant Name (1g)")
end
end
end
end
end

View File

@@ -0,0 +1,49 @@
# frozen_string_literal: true
require 'spec_helper'
require_relative '../../db/migrate/20241011071014_update_item_name_to_product_in_od_report'
RSpec.describe UpdateItemNameToProductInOdReport, type: :migration do
let!(:report_option_without_item_name_product) do
create(
:orders_and_distributors_options,
options: { fields_to_show: ['other_field'] }
)
end
describe '#up' do
let!(:report_option_with_item_name) do
create(
:orders_and_distributors_options,
options: { fields_to_show: ['item_name', 'other_field'] }
)
end
before { subject.up }
it 'updates fields_to_show from item_name to product only if options have item_name' do
report_option_with_item_name.reload
expect(fields_to_show(report_option_with_item_name)).to eq(['other_field', 'product'])
expect(fields_to_show(report_option_without_item_name_product)).to eq(['other_field'])
end
end
describe '#down' do
let!(:report_option_with_product) do
create(
:orders_and_distributors_options,
options: { fields_to_show: ['product', 'other_field'] }
)
end
before { subject.down }
it 'reverts fields_to_show from product to item_name only if options have product' do
report_option_with_product.reload
expect(fields_to_show(report_option_with_product)).to eq(['other_field', 'item_name'])
expect(fields_to_show(report_option_without_item_name_product)).to eq(['other_field'])
end
end
def fields_to_show(report_options)
report_options.options[:fields_to_show]
end
end

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
require 'spec_helper'
require_relative '../../db/migrate/20241030025540_copy_subject_on_semantic_links'
RSpec.describe CopySubjectOnSemanticLinks do
describe "#up" do
let(:original_variant) { create(:variant) }
let(:dummy_variant) { create(:variant) }
it "copies the original data" do
link = SemanticLink.create!(
subject: dummy_variant, # This would be NULL when migration runs.
semantic_id: "some-url",
)
SemanticLink.update_all("variant_id = #{original_variant.id}")
expect { subject.up }.to change {
link.reload.subject
}.from(dummy_variant).to(original_variant)
end
end
end

View File

@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Exchange do
it { is_expected.to have_many :semantic_links }
it "should be valid when built from factory" do
expect(build(:exchange)).to be_valid
end

View File

@@ -3,6 +3,6 @@
require 'spec_helper'
RSpec.describe SemanticLink, type: :model do
it { is_expected.to belong_to :variant }
it { is_expected.to belong_to :subject }
it { is_expected.to validate_presence_of(:semantic_id) }
end

View File

@@ -6,6 +6,9 @@ RSpec.describe Spree::Order do
let(:user) { build(:user, email: "spree@example.com") }
let(:order) { build(:order, user:) }
it { is_expected.to have_one :exchange }
it { is_expected.to have_many :semantic_links }
describe "#errors" do
it "provides friendly error messages" do
order.ship_address = Spree::Address.new

View File

@@ -27,7 +27,7 @@ RSpec.describe FdcBackorderer do
# After closing the order at the end, the test can be repeated live again.
# Build a new order when no open one is found:
order.order_cycle = build(:order_cycle)
order.order_cycle = create(:order_cycle, distributors: [order.distributor])
backorder = subject.find_or_build_order(order)
expect(backorder.semanticId).to eq urls.orders_url
expect(backorder.lines).to eq []
@@ -50,10 +50,19 @@ RSpec.describe FdcBackorderer do
expect(found_backorder.lines.count).to eq 1
expect(found_backorder.lines[0].quantity.to_i).to eq 3
# Without a stored semantic link, it can't look it up directly though:
found_backorder = subject.lookup_open_order(order)
expect(found_backorder).to eq nil
# But with a semantic link, it works:
order.exchange.semantic_links.create!(semantic_id: placed_order.semanticId)
found_backorder = subject.lookup_open_order(order)
expect(found_backorder.semanticId).to eq placed_order.semanticId
# And close the order again:
subject.complete_order(placed_order)
remaining_open_order = subject.find_or_build_order(order)
expect(remaining_open_order.semanticId).not_to eq placed_order.semanticId
expect(remaining_open_order.semanticId).to eq urls.orders_url
end
describe "#find_or_build_order" do

View File

@@ -27,7 +27,7 @@ RSpec.describe "Orders And Distributors" do
context "as an enterprise user" do
let(:header) {
["Order date", "Order Id", "Customer Name", "Customer Email", "Customer Phone",
"Customer City", "SKU", "Item name", "Variant", "Quantity", "Max Quantity",
"Customer City", "SKU", "Product", "Variant", "Quantity", "Max Quantity",
"Cost", "Shipping Cost", "Payment Method", "Distributor", "Distributor address",
"Distributor city", "Distributor postcode", "Shipping Method",
"Shipping instructions"]