feat: add edit/sync support for Log Quantities
er, just Standard Quantities so far..and just supported enough to move the ball forward, it still needs lots more polish
This commit is contained in:
parent
1d303a818c
commit
a43f98c304
8 changed files with 555 additions and 26 deletions
|
|
@ -151,6 +151,32 @@ class WuttaFarmAppHandler(base.AppHandler):
|
|||
factory = self.load_object(spec)
|
||||
return factory(self.config, farmos_client)
|
||||
|
||||
def get_quantity_types(self, session=None):
|
||||
"""
|
||||
Returns a list of all known quantity types.
|
||||
"""
|
||||
model = self.model
|
||||
with self.short_session(session=session) as sess:
|
||||
return (
|
||||
sess.query(model.QuantityType).order_by(model.QuantityType.name).all()
|
||||
)
|
||||
|
||||
def get_measures(self, session=None):
|
||||
"""
|
||||
Returns a list of all known measures.
|
||||
"""
|
||||
model = self.model
|
||||
with self.short_session(session=session) as sess:
|
||||
return sess.query(model.Measure).order_by(model.Measure.name).all()
|
||||
|
||||
def get_units(self, session=None):
|
||||
"""
|
||||
Returns a list of all known units.
|
||||
"""
|
||||
model = self.model
|
||||
with self.short_session(session=session) as sess:
|
||||
return sess.query(model.Unit).order_by(model.Unit.name).all()
|
||||
|
||||
def auto_sync_to_farmos(self, obj, model_name=None, client=None, require=True):
|
||||
"""
|
||||
Export the given object to farmOS, using configured handler.
|
||||
|
|
|
|||
|
|
@ -181,9 +181,13 @@ class Quantity(model.Base):
|
|||
creator=make_log_quantity,
|
||||
)
|
||||
|
||||
def get_value_decimal(self):
|
||||
# TODO: should actually return a decimal here?
|
||||
return self.value_numerator / self.value_denominator
|
||||
|
||||
def render_as_text(self, config=None):
|
||||
measure = str(self.measure or self.measure_id or "")
|
||||
value = self.value_numerator / self.value_denominator
|
||||
value = self.get_value_decimal()
|
||||
if config:
|
||||
app = config.get_app()
|
||||
value = app.render_quantity(value)
|
||||
|
|
|
|||
|
|
@ -437,21 +437,46 @@ class AssetRefs(WuttaSet):
|
|||
return AssetRefsWidget(self.request, **kwargs)
|
||||
|
||||
|
||||
class LogQuantityRefs(WuttaSet):
|
||||
class QuantityRefs(colander.List):
|
||||
"""
|
||||
Schema type for Quantities field (on a Log record)
|
||||
"""
|
||||
|
||||
def __init__(self, request):
|
||||
super().__init__()
|
||||
self.request = request
|
||||
self.config = self.request.wutta_config
|
||||
self.app = self.config.get_app()
|
||||
|
||||
def serialize(self, node, appstruct):
|
||||
if not appstruct:
|
||||
return colander.null
|
||||
|
||||
return {qty.uuid for qty in appstruct}
|
||||
quantities = []
|
||||
for qty in appstruct:
|
||||
quantities.append(
|
||||
{
|
||||
"uuid": qty.uuid.hex,
|
||||
"quantity_type": {
|
||||
"id": qty.quantity_type_id,
|
||||
"name": qty.quantity_type.name,
|
||||
},
|
||||
"measure": qty.measure_id,
|
||||
"value": qty.get_value_decimal(),
|
||||
"units": {
|
||||
"uuid": qty.units.uuid.hex,
|
||||
"name": qty.units.name,
|
||||
},
|
||||
"as_text": qty.render_as_text(self.config),
|
||||
}
|
||||
)
|
||||
|
||||
return quantities
|
||||
|
||||
def widget_maker(self, **kwargs):
|
||||
from wuttafarm.web.forms.widgets import LogQuantityRefsWidget
|
||||
from wuttafarm.web.forms.widgets import QuantityRefsWidget
|
||||
|
||||
return LogQuantityRefsWidget(self.request, **kwargs)
|
||||
return QuantityRefsWidget(self.request, **kwargs)
|
||||
|
||||
|
||||
class OwnerRefs(WuttaSet):
|
||||
|
|
|
|||
|
|
@ -526,11 +526,19 @@ class AssetRefsWidget(Widget):
|
|||
return set(pstruct.split(","))
|
||||
|
||||
|
||||
class LogQuantityRefsWidget(WuttaCheckboxChoiceWidget):
|
||||
class QuantityRefsWidget(Widget):
|
||||
"""
|
||||
Widget for Quantities field (on a Log record)
|
||||
"""
|
||||
|
||||
template = "quantityrefs"
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.request = request
|
||||
self.config = self.request.wutta_config
|
||||
self.app = self.config.get_app()
|
||||
|
||||
def serialize(self, field, cstruct, **kw):
|
||||
""" """
|
||||
model = self.app.model
|
||||
|
|
@ -538,24 +546,71 @@ class LogQuantityRefsWidget(WuttaCheckboxChoiceWidget):
|
|||
|
||||
readonly = kw.get("readonly", self.readonly)
|
||||
if readonly:
|
||||
if not cstruct:
|
||||
return ""
|
||||
|
||||
quantities = []
|
||||
for uuid in cstruct or []:
|
||||
qty = session.get(model.Quantity, uuid)
|
||||
quantities.append(
|
||||
HTML.tag(
|
||||
"li",
|
||||
c=tags.link_to(
|
||||
qty.render_as_text(self.config),
|
||||
# TODO
|
||||
self.request.route_url(
|
||||
"quantities_standard.view", uuid=qty.uuid
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
for qty in cstruct:
|
||||
# TODO: support more quantity types
|
||||
url = self.request.route_url(
|
||||
"quantities_standard.view", uuid=qty["uuid"]
|
||||
)
|
||||
quantities.append(HTML.tag("li", c=tags.link_to(qty["as_text"], url)))
|
||||
|
||||
return HTML.tag("ul", c=quantities)
|
||||
|
||||
return super().serialize(field, cstruct, **kw)
|
||||
tmpl_values = self.get_template_values(field, cstruct, kw)
|
||||
return field.renderer(self.template, **tmpl_values)
|
||||
|
||||
def get_template_values(self, field, cstruct, kw):
|
||||
model = self.app.model
|
||||
session = Session()
|
||||
values = super().get_template_values(field, cstruct, kw)
|
||||
|
||||
qtypes = []
|
||||
for qtype in self.app.get_quantity_types(session):
|
||||
# TODO: add support for other quantity types
|
||||
if qtype.drupal_id == "standard":
|
||||
qtypes.append(
|
||||
{
|
||||
"uuid": qtype.uuid.hex,
|
||||
"drupal_id": qtype.drupal_id,
|
||||
"name": qtype.name,
|
||||
}
|
||||
)
|
||||
values["quantity_types"] = qtypes
|
||||
|
||||
measures = []
|
||||
for measure in self.app.get_measures(session):
|
||||
measures.append(
|
||||
{
|
||||
"uuid": measure.uuid.hex,
|
||||
"drupal_id": measure.drupal_id,
|
||||
"name": measure.name,
|
||||
}
|
||||
)
|
||||
values["measures"] = measures
|
||||
|
||||
units = []
|
||||
for unit in self.app.get_units(session):
|
||||
units.append(
|
||||
{
|
||||
"uuid": unit.uuid.hex,
|
||||
"drupal_id": unit.drupal_id,
|
||||
"name": unit.name,
|
||||
}
|
||||
)
|
||||
values["units"] = units
|
||||
|
||||
return values
|
||||
|
||||
def deserialize(self, field, pstruct):
|
||||
""" """
|
||||
if not pstruct:
|
||||
return set()
|
||||
|
||||
return json.loads(pstruct)
|
||||
|
||||
|
||||
class OwnerRefsWidget(WuttaCheckboxChoiceWidget):
|
||||
|
|
|
|||
13
src/wuttafarm/web/templates/deform/quantityrefs.pt
Normal file
13
src/wuttafarm/web/templates/deform/quantityrefs.pt
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<div tal:define="
|
||||
name name|field.name;
|
||||
oid oid|field.oid;
|
||||
vmodel vmodel|'modelData.'+oid;"
|
||||
tal:omit-tag="">
|
||||
|
||||
<quantities-editor tal:attributes="name name;
|
||||
v-model vmodel;
|
||||
:quantity-types quantity_types;
|
||||
:measures measures;
|
||||
:units units;" />
|
||||
|
||||
</div>
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
<%def name="make_wuttafarm_components()">
|
||||
${self.make_assets_picker_component()}
|
||||
${self.make_animal_type_picker_component()}
|
||||
${self.make_quantity_editor_component()}
|
||||
${self.make_quantities_editor_component()}
|
||||
${self.make_plant_types_picker_component()}
|
||||
${self.make_seasons_picker_component()}
|
||||
</%def>
|
||||
|
|
@ -239,6 +241,356 @@
|
|||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_quantity_editor_component()">
|
||||
<script type="text/x-template" id="quantity-editor-template">
|
||||
<div>
|
||||
|
||||
<b-field label="Measure" horizontal
|
||||
## TODO: why is this needed?
|
||||
style="margin-bottom: 1rem;">
|
||||
<b-select v-model="value.measure"
|
||||
ref="measure">
|
||||
<option v-for="m in measures"
|
||||
:value="m.drupal_id">
|
||||
{{ m.name }}
|
||||
</option>
|
||||
</b-field>
|
||||
|
||||
<b-field label="Value" horizontal
|
||||
## TODO: why is this needed?
|
||||
style="margin-bottom: 1rem;">
|
||||
<b-input v-model="value.value"
|
||||
ref="value"
|
||||
@keydown.native="inputKeydown" />
|
||||
</b-field>
|
||||
|
||||
<b-field label="Units" horizontal
|
||||
style="margin-bottom: 1rem;">
|
||||
|
||||
<b-button v-if="value.units.uuid"
|
||||
@click="changeUnit()">
|
||||
{{ value.units.name }} (click to change)
|
||||
</b-button>
|
||||
|
||||
<b-autocomplete v-show="!value.units.uuid"
|
||||
v-model="unitsName"
|
||||
ref="unitsName"
|
||||
:data="unitsNameData"
|
||||
field="name"
|
||||
open-on-focus
|
||||
keep-first
|
||||
@select="unitsNameSelected"
|
||||
clear-on-select>
|
||||
<template #empty>No results found</template>
|
||||
</b-autocomplete>
|
||||
|
||||
</b-field>
|
||||
|
||||
<div class="buttons" style="margin-left: 8rem;">
|
||||
<b-button type="is-primary"
|
||||
ref="save"
|
||||
@click="save"
|
||||
:disabled="saveDisabled">
|
||||
{{ creating ? "Create" : "Update" }} Quantity
|
||||
</b-button>
|
||||
<b-button @click="$emit('cancel')">
|
||||
Cancel
|
||||
</b-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
const QuantityEditor = {
|
||||
template: '#quantity-editor-template',
|
||||
props: {
|
||||
name: String,
|
||||
value: Object,
|
||||
measures: Array,
|
||||
units: Array,
|
||||
creating: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
|
||||
return {
|
||||
measure: this.value.measure,
|
||||
valueAmount: this.value.value,
|
||||
unit: this.value.units,
|
||||
unitsName: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
saveDisabled() {
|
||||
if (!this.value.measure) {
|
||||
return true
|
||||
}
|
||||
if (!this.value.value) {
|
||||
return true
|
||||
}
|
||||
if (!this.value.units.uuid) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
unitsNameData() {
|
||||
if (!this.unitsName) {
|
||||
return this.units
|
||||
}
|
||||
return this.units.filter((unit) => {
|
||||
return unit.name.toLowerCase().indexOf(this.unitsName.toLowerCase()) >= 0
|
||||
})
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
focusMeasure() {
|
||||
this.$refs.measure.focus()
|
||||
},
|
||||
|
||||
focusValue() {
|
||||
this.$refs.value.focus()
|
||||
},
|
||||
|
||||
changeUnit() {
|
||||
this.value.units = {}
|
||||
this.$emit('input', this.value)
|
||||
this.$nextTick(() => {
|
||||
this.$refs.unitsName.focus()
|
||||
})
|
||||
},
|
||||
|
||||
unitsNameSelected(option) {
|
||||
if (option) {
|
||||
this.value.units = option
|
||||
this.$emit('input', this.value)
|
||||
this.unitsName = null
|
||||
if (this.value.measure && this.value.value) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.save.$el.focus()
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
inputKeydown(event) {
|
||||
// nb. must prevent main form submit on ENTER
|
||||
// (since ultimately this lives within an outer form)
|
||||
// but also we can submit the modal pseudo-form
|
||||
if (event.which == 13) {
|
||||
event.preventDefault()
|
||||
this.save()
|
||||
}
|
||||
},
|
||||
|
||||
save() {
|
||||
this.$emit('save', this.value)
|
||||
},
|
||||
},
|
||||
}
|
||||
Vue.component('quantity-editor', QuantityEditor)
|
||||
<% request.register_component('quantity-editor', 'QuantityEditor') %>
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_quantities_editor_component()">
|
||||
<script type="text/x-template" id="quantities-editor-template">
|
||||
<div>
|
||||
<input type="hidden" :name="name" :value="value ? JSON.stringify(value) : ''" />
|
||||
|
||||
<${b}-table ref="table"
|
||||
:data="value || []"
|
||||
detailed
|
||||
detail-key="uuid"
|
||||
icon-pack="fas"
|
||||
:show-detail-icon="false">
|
||||
|
||||
<${b}-table-column field="as_text"
|
||||
v-slot="props"
|
||||
label="Quantity">
|
||||
<span>{{ props.row.as_text }}</span>
|
||||
</${b}-table-column>
|
||||
|
||||
<${b}-table-column field="quantity_type"
|
||||
v-slot="props"
|
||||
label="Quantity Type">
|
||||
<span>{{ props.row.quantity_type.name }}</span>
|
||||
</${b}-table-column>
|
||||
|
||||
<${b}-table-column v-slot="props">
|
||||
<div v-show="!editing[props.row.uuid]">
|
||||
<a href="#"
|
||||
@click.prevent="editInit(props.row)">
|
||||
<i class="fas fa-edit" /> Edit
|
||||
</a>
|
||||
|
||||
<a href="#"
|
||||
class="has-text-danger"
|
||||
@click.prevent="removeQuantity(props.row)">
|
||||
<i class="fas fa-trash" /> Remove
|
||||
</a>
|
||||
</div>
|
||||
</${b}-table-column>
|
||||
|
||||
<template #detail="props">
|
||||
<quantity-editor v-model="props.row"
|
||||
:measures="measures"
|
||||
:units="units"
|
||||
@save="editSave"
|
||||
@cancel="editCancel(props.row)" />
|
||||
</template>
|
||||
|
||||
</${b}-table>
|
||||
|
||||
<b-field grouped
|
||||
v-show="!creating">
|
||||
|
||||
<b-select v-model="quantityType">
|
||||
<option v-for="qtype of quantityTypes"
|
||||
:value="qtype.drupal_id">
|
||||
{{ qtype.name }}
|
||||
</option>
|
||||
</b-select>
|
||||
|
||||
<b-button type="is-primary"
|
||||
icon-pack="fas"
|
||||
icon-left="plus"
|
||||
@click="createInit()">
|
||||
Add New Quantity
|
||||
</b-button>
|
||||
|
||||
</b-field>
|
||||
|
||||
<quantity-editor v-show="creating"
|
||||
v-model="newQuantity"
|
||||
ref="newQuantity"
|
||||
creating
|
||||
@save="createSave"
|
||||
@cancel="createCancel"
|
||||
:measures="measures"
|
||||
:units="units" />
|
||||
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
const QuantitiesEditor = {
|
||||
template: '#quantities-editor-template',
|
||||
props: {
|
||||
name: String,
|
||||
value: Array,
|
||||
quantityTypes: Array,
|
||||
defaultQuantityType: {
|
||||
type: String,
|
||||
default: 'standard',
|
||||
},
|
||||
measures: Array,
|
||||
units: Array,
|
||||
},
|
||||
data() {
|
||||
|
||||
const editing = {}
|
||||
if (this.value) {
|
||||
for (qty of this.value) {
|
||||
editing[qty.uuid] = false
|
||||
}
|
||||
}
|
||||
|
||||
const measureMap = {}
|
||||
for (let m of this.measures) {
|
||||
measureMap[m.drupal_id] = m.name
|
||||
}
|
||||
|
||||
return {
|
||||
measureMap,
|
||||
quantityType: this.defaultQuantityType,
|
||||
editShowDialog: false,
|
||||
editNew: true,
|
||||
editQuantity: null,
|
||||
editMeasure: null,
|
||||
editValue: null,
|
||||
editUnits: null,
|
||||
editing: editing,
|
||||
creating: false,
|
||||
newQuantity: {
|
||||
measure: null,
|
||||
value: null,
|
||||
units: {},
|
||||
},
|
||||
newCounter: 1,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
createSave(qty) {
|
||||
qty = Object.fromEntries(Object.entries(qty))
|
||||
|
||||
qty.uuid = 'new_' + this.newCounter++
|
||||
qty.as_text = "( " + this.measureMap[qty.measure] + " ) " + qty.value + " " + qty.units.name
|
||||
|
||||
const value = Array.from(this.value || [])
|
||||
value.push(qty)
|
||||
this.$emit('input', value)
|
||||
|
||||
this.creating = false
|
||||
},
|
||||
|
||||
createCancel() {
|
||||
this.creating = false
|
||||
},
|
||||
|
||||
createInit() {
|
||||
|
||||
this.newQuantity.quantity_type = {
|
||||
drupal_id: this.quantityType,
|
||||
## TODO: add support for other quantity types
|
||||
name: "Standard",
|
||||
}
|
||||
|
||||
this.newQuantity.measure = null
|
||||
this.newQuantity.value = null
|
||||
this.newQuantity.units = {}
|
||||
|
||||
this.creating = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.newQuantity.focusMeasure()
|
||||
})
|
||||
},
|
||||
|
||||
editInit(qty) {
|
||||
this.$refs.table.openDetailRow(qty)
|
||||
this.editing[qty.uuid] = true
|
||||
},
|
||||
|
||||
editSave(row) {
|
||||
row.as_text = "( " + this.measureMap[row.measure] + " ) " + row.value + " " + row.units.name
|
||||
this.$emit('input', this.value)
|
||||
this.editing[row.uuid] = false
|
||||
this.$refs.table.closeDetailRow(row)
|
||||
},
|
||||
|
||||
editCancel(qty) {
|
||||
this.$refs.table.closeDetailRow(qty)
|
||||
this.editing[qty.uuid] = false
|
||||
},
|
||||
|
||||
removeQuantity(qty) {
|
||||
let value = Array.from(this.value)
|
||||
const i = value.indexOf(qty)
|
||||
value.splice(i, 1)
|
||||
this.$emit('input', value)
|
||||
},
|
||||
},
|
||||
}
|
||||
Vue.component('quantities-editor', QuantitiesEditor)
|
||||
<% request.register_component('quantities-editor', 'QuantitiesEditor') %>
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="make_plant_types_picker_component()">
|
||||
<script type="text/x-template" id="plant-types-picker-template">
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ from wuttaweb.forms.widgets import WuttaDateTimeWidget
|
|||
|
||||
from wuttafarm.web.views import WuttaFarmMasterView
|
||||
from wuttafarm.db.model import LogType, Log
|
||||
from wuttafarm.web.forms.schema import AssetRefs, LogQuantityRefs, OwnerRefs
|
||||
from wuttafarm.web.forms.schema import AssetRefs, QuantityRefs, OwnerRefs
|
||||
from wuttafarm.util import get_log_type_enum
|
||||
|
||||
|
||||
|
|
@ -287,10 +287,8 @@ class LogMasterView(WuttaFarmMasterView):
|
|||
f.set_readonly("log_type")
|
||||
|
||||
# quantities
|
||||
if self.creating or self.editing:
|
||||
f.remove("quantities") # TODO: need to support this
|
||||
else:
|
||||
f.set_node("quantities", LogQuantityRefs(self.request))
|
||||
f.set_node("quantities", QuantityRefs(self.request))
|
||||
if not self.creating:
|
||||
# nb. must explicity declare value for non-standard field
|
||||
f.set_default("quantities", log.quantities)
|
||||
|
||||
|
|
@ -332,6 +330,7 @@ class LogMasterView(WuttaFarmMasterView):
|
|||
self.set_assets(log, data["assets"])
|
||||
self.set_locations(log, data["locations"])
|
||||
self.set_groups(log, data["groups"])
|
||||
self.set_quantities(log, data["quantities"])
|
||||
|
||||
return log
|
||||
|
||||
|
|
@ -386,6 +385,58 @@ class LogMasterView(WuttaFarmMasterView):
|
|||
assert group
|
||||
log.groups.remove(group)
|
||||
|
||||
def set_quantities(self, log, desired):
|
||||
model = self.app.model
|
||||
session = self.Session()
|
||||
|
||||
current = {qty.uuid.hex: qty for qty in log.quantities}
|
||||
for new_qty in desired:
|
||||
units = session.get(model.Unit, new_qty["units"]["uuid"])
|
||||
assert units
|
||||
if new_qty["uuid"].startswith("new_"):
|
||||
assert new_qty["quantity_type"]["drupal_id"] == "standard"
|
||||
factory = model.StandardQuantity
|
||||
qty = factory(
|
||||
quantity_type_id=new_qty["quantity_type"]["drupal_id"],
|
||||
measure_id=new_qty["measure"],
|
||||
value_numerator=int(new_qty["value"]),
|
||||
value_denominator=1,
|
||||
units=units,
|
||||
)
|
||||
# nb. must ensure "typed" quantity record persists!
|
||||
session.add(qty)
|
||||
# but must add "generic" quantity record to log
|
||||
log.quantities.append(qty.quantity)
|
||||
else:
|
||||
old_qty = current[new_qty["uuid"]]
|
||||
old_qty.measure_id = new_qty["measure"]
|
||||
old_qty.value_numerator = int(new_qty["value"])
|
||||
old_qty.value_denominator = 1
|
||||
old_qty.units = units
|
||||
|
||||
desired = [qty["uuid"] for qty in desired]
|
||||
for old_qty in list(log.quantities):
|
||||
# nb. "old_qty" may be newly-created, w/ no uuid yet
|
||||
# (this logic may break if session gets flushed early!)
|
||||
if old_qty.uuid and old_qty.uuid.hex not in desired:
|
||||
log.quantities.remove(old_qty)
|
||||
|
||||
def auto_sync_to_farmos(self, client, log):
|
||||
model = self.app.model
|
||||
session = self.Session()
|
||||
|
||||
# nb. ensure quantities have uuid keys
|
||||
session.flush()
|
||||
|
||||
for qty in log.quantities:
|
||||
# TODO: support more quantity types
|
||||
if qty.quantity_type_id == "standard":
|
||||
qty = session.get(model.StandardQuantity, qty.uuid)
|
||||
assert qty
|
||||
self.app.auto_sync_to_farmos(qty, client=client)
|
||||
|
||||
self.app.auto_sync_to_farmos(log, client=client)
|
||||
|
||||
def get_farmos_url(self, log):
|
||||
return self.app.get_farmos_url(f"/log/{log.drupal_id}")
|
||||
|
||||
|
|
|
|||
|
|
@ -113,6 +113,9 @@ class WuttaFarmMasterView(MasterView):
|
|||
# maybe also sync change to farmOS
|
||||
if self.app.is_farmos_mirror():
|
||||
client = get_farmos_client_for_user(self.request)
|
||||
self.auto_sync_to_farmos(client, obj)
|
||||
|
||||
def auto_sync_to_farmos(self, client, obj):
|
||||
self.app.auto_sync_to_farmos(obj, client=client, require=False)
|
||||
|
||||
def get_farmos_entity_type(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue