master #219

Merged
mpeltriaux merged 5 commits from master into Docker 2022-10-11 16:41:06 +02:00
7 changed files with 118 additions and 51 deletions

View File

@ -0,0 +1,36 @@
# Generated by Django 3.1.3 on 2022-10-11 11:39
from django.db import migrations, models
from django.db.models import Sum
def fill_deductable_rest(apps, schema_editor):
EcoAccount = apps.get_model("compensation", "EcoAccount")
accs = EcoAccount.objects.all()
for acc in accs:
deductions = acc.deductions.filter(
intervention__deleted=None,
)
deductions_surfaces = deductions.aggregate(Sum("surface"))["surface__sum"] or 0
available_surfaces = acc.deductable_surface or deductions_surfaces
rest = available_surfaces - deductions_surfaces
acc.deductable_rest = rest
acc.save()
class Migration(migrations.Migration):
dependencies = [
('compensation', '0010_auto_20220815_1030'),
]
operations = [
migrations.AddField(
model_name='ecoaccount',
name='deductable_rest',
field=models.FloatField(blank=True, default=0, help_text='Amount of deductable rest', null=True),
),
migrations.RunPython(fill_deductable_rest)
]

View File

@ -35,6 +35,12 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix
help_text="Amount of deductable surface - can be lower than the total surface due to deduction limitations", help_text="Amount of deductable surface - can be lower than the total surface due to deduction limitations",
default=0, default=0,
) )
deductable_rest = models.FloatField(
blank=True,
null=True,
help_text="Amount of deductable rest",
default=0,
)
legal = models.OneToOneField( legal = models.OneToOneField(
"intervention.Legal", "intervention.Legal",
@ -100,28 +106,22 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix
""" """
return self.after_states.all().aggregate(Sum("surface"))["surface__sum"] or 0 return self.after_states.all().aggregate(Sum("surface"))["surface__sum"] or 0
def get_available_rest(self) -> (float, float): def __calculate_deductable_rest(self):
""" Calculates available rest surface of the eco account """ Calculates available rest surface of the eco account
Args: Args:
Returns: Returns:
ret_val_total (float): Total amount ret_val_total (float): Total amount
ret_val_relative (float): Amount as percentage (0-100)
""" """
deductions = self.deductions.filter( deductions = self.deductions.filter(
intervention__deleted=None, intervention__deleted=None,
) )
deductions_surfaces = deductions.aggregate(Sum("surface"))["surface__sum"] or 0 deductions_surfaces = deductions.aggregate(Sum("surface"))["surface__sum"] or 0
available_surfaces = self.deductable_surface or deductions_surfaces ## no division by zero available_surfaces = self.deductable_surface or deductions_surfaces ## no division by zero
ret_val_total = available_surfaces - deductions_surfaces ret_val = available_surfaces - deductions_surfaces
if available_surfaces > 0: return ret_val
ret_val_relative = int((ret_val_total / available_surfaces) * 100)
else:
ret_val_relative = 0
return ret_val_total, ret_val_relative
def quality_check(self) -> EcoAccountQualityChecker: def quality_check(self) -> EcoAccountQualityChecker:
""" Quality check """ Quality check
@ -181,6 +181,29 @@ class EcoAccount(AbstractCompensation, ShareableObjectMixin, RecordableObjectMix
for team_id in shared_teams: for team_id in shared_teams:
celery_send_mail_deduction_changed_team.delay(self.identifier, self.title, team_id, data_change) celery_send_mail_deduction_changed_team.delay(self.identifier, self.title, team_id, data_change)
def update_deductable_rest(self):
"""
Updates deductable_rest, which holds the amount of rest surface for this account.
Returns:
"""
self.deductable_rest = self.__calculate_deductable_rest()
self.save()
def get_deductable_rest_relative(self):
"""
Returns deductable_rest relative to deductable_surface mapped to [0,100]
Returns:
"""
try:
ret_val = int((self.deductable_rest / (self.deductable_surface or 0)) * 100)
except ZeroDivisionError:
ret_val = 0
return ret_val
class EcoAccountDocument(AbstractDocument): class EcoAccountDocument(AbstractDocument):
""" """
@ -272,3 +295,8 @@ class EcoAccountDeduction(BaseResource):
self.intervention.mark_as_edited(user, edit_comment=DEDUCTION_REMOVED) self.intervention.mark_as_edited(user, edit_comment=DEDUCTION_REMOVED)
self.account.mark_as_edited(user, edit_comment=DEDUCTION_REMOVED) self.account.mark_as_edited(user, edit_comment=DEDUCTION_REMOVED)
super().delete(*args, **kwargs) super().delete(*args, **kwargs)
self.account.update_deductable_rest()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.account.update_deductable_rest()

View File

@ -37,7 +37,7 @@ class EcoAccountTable(BaseTable, TableRenderMixin):
av = tables.Column( av = tables.Column(
verbose_name=_("Available"), verbose_name=_("Available"),
orderable=True, orderable=True,
empty_values=[], accessor="deductable_rest",
attrs={ attrs={
"th": { "th": {
"class": "w-20", "class": "w-20",
@ -100,13 +100,16 @@ class EcoAccountTable(BaseTable, TableRenderMixin):
""" Renders the available column for an eco account """ Renders the available column for an eco account
Args: Args:
value (str): The identifier value value (float): The deductable_rest
record (EcoAccount): The eco account record record (EcoAccount): The eco account record
Returns: Returns:
""" """
value_total, value_relative = record.get_available_rest() try:
value_relative = record.get_deductable_rest_relative()
except ZeroDivisionError:
value_relative = 0
html = render_to_string("konova/widgets/progressbar.html", {"value": value_relative}) html = render_to_string("konova/widgets/progressbar.html", {"value": value_relative})
return format_html(html) return format_html(html)

View File

@ -230,7 +230,7 @@ class EcoAccountWorkflowTestCase(BaseWorkflowTestCase):
self.assertTrue(self.intervention.log.first().action == UserAction.EDITED) self.assertTrue(self.intervention.log.first().action == UserAction.EDITED)
def test_edit_deduction(self): def test_edit_deduction(self):
test_surface = self.eco_account.get_available_rest()[0] test_surface = self.eco_account.deductable_rest
self.eco_account.set_recorded(self.superuser) self.eco_account.set_recorded(self.superuser)
self.intervention.share_with_user(self.superuser) self.intervention.share_with_user(self.superuser)
self.eco_account.refresh_from_db() self.eco_account.refresh_from_db()

View File

@ -200,7 +200,8 @@ def detail_view(request: HttpRequest, id: str):
sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0 sum_after_states = after_states.aggregate(Sum("surface"))["surface__sum"] or 0
diff_states = abs(sum_before_states - sum_after_states) diff_states = abs(sum_before_states - sum_after_states)
# Calculate rest of available surface for deductions # Calculate rest of available surface for deductions
available_total, available_relative = acc.get_available_rest() available_total = acc.deductable_rest
available_relative = acc.get_deductable_rest_relative()
# Prefetch related data to decrease the amount of db connections # Prefetch related data to decrease the amount of db connections
deductions = acc.deductions.filter( deductions = acc.deductions.filter(

View File

@ -221,8 +221,17 @@ class Geometry(BaseResource):
polygons.append(p) polygons.append(p)
geojson = { geojson = {
"type": "FeatureCollection", "type": "FeatureCollection",
"crs": {
"type": "name",
"properties": {
"name": f"urn:ogc:def:crs:EPSG::{geom.srid}"
}
},
"features": [ "features": [
json.loads(x.geojson) for x in polygons {
"type": "Feature",
"geometry": json.loads(x.geojson)
} for x in polygons
] ]
} }
return geojson return geojson

View File

@ -394,16 +394,12 @@ netgis.MapOpenLayers.prototype.clearAll = function()
{ {
for ( var i = 0; i < this.layers.length; i++ ) for ( var i = 0; i < this.layers.length; i++ )
{ {
if(this.layers[i] === this.editLayer){
continue;
}
this.map.removeLayer( this.layers[ i ] ); this.map.removeLayer( this.layers[ i ] );
} }
this.layers = [this.editLayer]; this.layers = [];
this.snapFeatures.clear(); this.snapFeatures.clear();
this.snapFeatures.push(this.editLayer);
}; };
netgis.MapOpenLayers.prototype.onUpdateStyle = function( e ) netgis.MapOpenLayers.prototype.onUpdateStyle = function( e )
@ -1145,14 +1141,8 @@ netgis.MapOpenLayers.prototype.updateEditLayerItem = function()
netgis.MapOpenLayers.prototype.onEditFeaturesLoaded = function( e ) netgis.MapOpenLayers.prototype.onEditFeaturesLoaded = function( e )
{ {
var json = e; var json = e;
var format = new ol.format.GeoJSON(); var self = this;
var features = format.readFeatures( json ); window.setTimeout( function() { self.createLayerGeoJSON( "Import", json ); }, 10 );
this.editLayer.getSource().addFeatures( features );
//this.snapFeatures.push( e.feature );
if ( features.length > 0 )
this.view.fit( this.editLayer.getSource().getExtent(), { padding: [ 40, 40, 40, 40 ] } );
}; };
netgis.MapOpenLayers.prototype.onDragEnter = function( e ) netgis.MapOpenLayers.prototype.onDragEnter = function( e )
@ -1267,6 +1257,7 @@ netgis.MapOpenLayers.prototype.createLayerGeoJSON = function( title, data )
//NOTE: netgis.util.foreach( proj4.defs, function( k,v ) { console.info( "DEF:", k, v ); } ) //NOTE: netgis.util.foreach( proj4.defs, function( k,v ) { console.info( "DEF:", k, v ); } )
var projcode = projection.getCode(); var projcode = projection.getCode();
switch ( projcode ) switch ( projcode )
{ {
case "EPSG:3857": case "EPSG:3857":
@ -1452,7 +1443,6 @@ netgis.MapOpenLayers.prototype.createLayerShapefile = function( title, shapeData
netgis.MapOpenLayers.prototype.addImportedFeatures = function( features ) netgis.MapOpenLayers.prototype.addImportedFeatures = function( features )
{ {
// ToDO: Changes in here problematic on initial data loading
// Add To Edit Layer // Add To Edit Layer
this.editEventsSilent = true; this.editEventsSilent = true;
this.editLayer.getSource().addFeatures( features ); this.editLayer.getSource().addFeatures( features );