55
66from faker import Faker
77
8- from odoo import fields , models
8+ from odoo import _ , api , fields , models
9+ from odoo .exceptions import ValidationError
910
1011_logger = logging .getLogger (__name__ )
1112
@@ -16,28 +17,28 @@ class SPPDemoDataGenerator(models.Model):
1617 # Farmer Registry specific fields
1718 percentage_with_farm_details = fields .Integer (
1819 string = "% with Farm Details" ,
19- default = 80 ,
20+ default = 100 ,
2021 required = True ,
2122 help = "Percentage of farmers that will have farm details" ,
2223 )
2324 percentage_with_land_records = fields .Integer (
2425 string = "% with Land Records" ,
25- default = 70 ,
26+ default = 100 ,
2627 required = True ,
2728 help = "Percentage of farmers that will have land records" ,
2829 )
2930 percentage_with_farm_assets = fields .Integer (
30- string = "% with Farm Assets" , default = 60 , required = True , help = "Percentage of farmers that will have farm assets"
31+ string = "% with Farm Assets" , default = 100 , required = True , help = "Percentage of farmers that will have farm assets"
3132 )
3233 percentage_with_agricultural_activities = fields .Integer (
3334 string = "% with Agricultural Activities" ,
34- default = 85 ,
35+ default = 100 ,
3536 required = True ,
3637 help = "Percentage of farmers that will have agricultural activities" ,
3738 )
3839 percentage_with_extension_services = fields .Integer (
3940 string = "% with Extension Services" ,
40- default = 40 ,
41+ default = 100 ,
4142 required = True ,
4243 help = "Percentage of farmers that will have extension services" ,
4344 )
@@ -66,6 +67,34 @@ class SPPDemoDataGenerator(models.Model):
6667 help = "Maximum number of agricultural activities per farm" ,
6768 )
6869
70+ # Season configuration fields
71+ season_name = fields .Char (
72+ string = "Season Name" ,
73+ default = lambda self : f"Demo Season { fields .Date .today ().year } " ,
74+ required = True ,
75+ help = "Name of the agricultural season to use or create" ,
76+ )
77+ season_start_date = fields .Date (
78+ string = "Season Start Date" ,
79+ default = lambda self : fields .Date .today ().replace (month = 1 , day = 1 ),
80+ required = True ,
81+ help = "Start date of the agricultural season" ,
82+ )
83+ season_end_date = fields .Date (
84+ string = "Season End Date" ,
85+ default = lambda self : fields .Date .today ().replace (month = 12 , day = 31 ),
86+ required = True ,
87+ help = "End date of the agricultural season" ,
88+ )
89+
90+ @api .constrains ("season_start_date" , "season_end_date" )
91+ def _check_season_dates (self ):
92+ """Validate that season end date is after start date"""
93+ for record in self :
94+ if record .season_end_date and record .season_start_date :
95+ if record .season_end_date < record .season_start_date :
96+ raise ValidationError (_ ("Season end date must be after start date" ))
97+
6998 # Selection options for farmer registry
7099 FARM_TYPES = [
71100 ("crop" , "Crop" ),
@@ -119,7 +148,21 @@ def generate_demo_data(self):
119148 self ._generate_chemical_data (fake )
120149 self ._generate_fertilizer_data (fake )
121150 self ._generate_feed_items_data (fake )
122- self ._generate_season_data (fake )
151+
152+ # Validate that season can be created/found
153+ season = self ._generate_season_data (fake )
154+ if not season :
155+ raise ValidationError (
156+ _ (
157+ "Failed to create or find an active agricultural season. "
158+ "Please check the season configuration and try again."
159+ )
160+ )
161+
162+ _logger .info (
163+ f"Demo data generation initialized with season: { season .name } "
164+ f"({ season .date_start } to { season .date_end } )"
165+ )
123166
124167 result = super ().generate_demo_data ()
125168 return result
@@ -147,9 +190,11 @@ def get_group_vals(self, fake):
147190 )
148191
149192 # Specific Head Farmer details on Group
193+ farmer_family_name = fake .last_name ()
150194 group_vals .update (
151195 {
152- "farmer_family_name" : fake .last_name (),
196+ "farmer_family_name" : farmer_family_name ,
197+ "name" : farmer_family_name ,
153198 "farmer_given_name" : fake .first_name (),
154199 "farmer_addtnl_name" : fake .first_name () if random .choice ([True , False ]) else None ,
155200 "farmer_mobile_tel" : self .generate_phone_number (fake ),
@@ -541,7 +586,7 @@ def _generate_land_records(self, fake, group):
541586 """Generate land records for a farm"""
542587 num_parcels = random .randint (1 , self .max_land_parcels_per_farm )
543588
544- for _ in range (num_parcels ):
589+ for _i in range (num_parcels ):
545590 land_vals = self ._get_land_record_vals (fake , group )
546591 land_record = self .env ["spp.land.record" ].create (land_vals )
547592
@@ -598,7 +643,7 @@ def _generate_farm_assets(self, fake, group):
598643 """Generate farm assets and machinery"""
599644 num_assets = random .randint (1 , self .max_assets_per_farm )
600645
601- for _ in range (num_assets ):
646+ for _i in range (num_assets ):
602647 # Generate farm assets
603648 asset_vals = self ._get_farm_asset_vals (fake , group )
604649 self .env ["spp.farm.asset" ].create (asset_vals )
@@ -629,20 +674,32 @@ def _get_machinery_vals(self, fake, group):
629674
630675 def _generate_agricultural_activities (self , fake , group ):
631676 """Generate agricultural activities"""
677+ # Ensure season is available
678+ season_id = self ._get_random_season ()
679+ if not season_id :
680+ _logger .warning (f"No active season available for group { group .id } . Skipping agricultural activities." )
681+ return
682+
683+ season = self .env ["spp.farm.season" ].browse (season_id )
684+ _logger .debug (f"Generating agricultural activities for group { group .id } using season: { season .name } " )
685+
632686 num_activities = random .randint (1 , self .max_activities_per_farm )
633687
634- for _ in range (num_activities ):
635- activity_vals = self ._get_agricultural_activity_vals (fake , group )
636- self .env ["spp.farm.activity" ].create (activity_vals )
688+ for _i in range (num_activities ):
689+ activity_vals = self ._get_agricultural_activity_vals (fake , group , season_id )
690+ try :
691+ self .env ["spp.farm.activity" ].create (activity_vals )
692+ except Exception as e :
693+ _logger .error (f"Failed to create agricultural activity for group { group .id } : { str (e )} " )
637694
638- def _get_agricultural_activity_vals (self , fake , group ):
695+ def _get_agricultural_activity_vals (self , fake , group , season_id ):
639696 """Get agricultural activity values"""
640697 activity_type = random .choice (self .ACTIVITY_TYPES )
641698
642699 vals = {
643700 "activity_type" : activity_type [0 ],
644701 "purpose" : random .choice (self .PRODUCTION_PURPOSES )[0 ],
645- "season_id" : self . _get_random_season () ,
702+ "season_id" : season_id ,
646703 }
647704
648705 # Set the appropriate farm field based on activity type
@@ -708,7 +765,7 @@ def _generate_extension_services(self, fake, group):
708765 """Generate farm extension services"""
709766 num_services = random .randint (1 , 3 )
710767
711- for _ in range (num_services ):
768+ for _i in range (num_services ):
712769 extension_vals = self ._get_extension_service_vals (fake , group )
713770 self .env ["spp.farm.extension" ].create (extension_vals )
714771
@@ -824,29 +881,81 @@ def _generate_feed_items_data(self, fake):
824881 self .env ["spp.feed.items" ].create (feed_info )
825882
826883 def _generate_season_data (self , fake ):
827- """Generate agricultural season data for the demo"""
828- current_year = fields .Date .today ().year
829- seasons = [
830- {
831- "name" : f"Season { current_year } " ,
832- "description" : f"Main agricultural season for { current_year } " ,
833- "date_start" : fields .Date .today ().replace (month = 1 , day = 1 ),
834- "date_end" : fields .Date .today ().replace (month = 12 , day = 31 ),
835- "state" : "active" ,
836- },
837- {
838- "name" : f"Season { current_year - 1 } " ,
839- "description" : f"Previous agricultural season for { current_year - 1 } " ,
840- "date_start" : fields .Date .today ().replace (year = current_year - 1 , month = 1 , day = 1 ),
841- "date_end" : fields .Date .today ().replace (year = current_year - 1 , month = 12 , day = 31 ),
842- "state" : "closed" ,
843- },
844- ]
884+ """Generate or find agricultural season data for the demo"""
885+ try :
886+ # Search for existing season matching name, dates, and active state
887+ existing_season = self .env ["spp.farm.season" ].search (
888+ [
889+ ("name" , "=" , self .season_name ),
890+ ("date_start" , "=" , self .season_start_date ),
891+ ("date_end" , "=" , self .season_end_date ),
892+ ("state" , "=" , "active" ),
893+ ],
894+ limit = 1 ,
895+ )
845896
846- for season_info in seasons :
847- existing = self .env ["spp.farm.season" ].search ([("name" , "=" , season_info ["name" ])])
848- if not existing :
849- self .env ["spp.farm.season" ].create (season_info )
897+ if existing_season :
898+ _logger .info (f"Using existing active season: { existing_season .name } (ID: { existing_season .id } )" )
899+ return existing_season
900+
901+ # Search for season with same name but different dates or status
902+ existing_season_by_name = self .env ["spp.farm.season" ].search (
903+ [
904+ ("name" , "=" , self .season_name ),
905+ ],
906+ limit = 1 ,
907+ )
908+
909+ if existing_season_by_name :
910+ # Update existing season if it's not closed
911+ if existing_season_by_name .state != "closed" :
912+ _logger .info (f"Updating existing season: { existing_season_by_name .name } " )
913+ existing_season_by_name .write (
914+ {
915+ "date_start" : self .season_start_date ,
916+ "date_end" : self .season_end_date ,
917+ }
918+ )
919+ if existing_season_by_name .state == "draft" :
920+ existing_season_by_name .action_activate ()
921+ _logger .info (
922+ f"Season updated and activated: { existing_season_by_name .name } "
923+ f"(ID: { existing_season_by_name .id } )"
924+ )
925+ return existing_season_by_name
926+ else :
927+ _logger .warning (
928+ f"Season '{ self .season_name } ' exists but is closed. Creating new season with modified name."
929+ )
930+ # Create new season with modified name
931+ season_vals = {
932+ "name" : f"{ self .season_name } (Demo)" ,
933+ "description" : f"Demo agricultural season created on { fields .Date .today ()} " ,
934+ "date_start" : self .season_start_date ,
935+ "date_end" : self .season_end_date ,
936+ "state" : "draft" ,
937+ }
938+ new_season = self .env ["spp.farm.season" ].create (season_vals )
939+ new_season .action_activate ()
940+ _logger .info (f"Created and activated new season: { new_season .name } (ID: { new_season .id } )" )
941+ return new_season
942+
943+ # Create new season if none exists
944+ season_vals = {
945+ "name" : self .season_name ,
946+ "description" : f"Demo agricultural season created on { fields .Date .today ()} " ,
947+ "date_start" : self .season_start_date ,
948+ "date_end" : self .season_end_date ,
949+ "state" : "draft" ,
950+ }
951+ new_season = self .env ["spp.farm.season" ].create (season_vals )
952+ new_season .action_activate ()
953+ _logger .info (f"Created and activated new season: { new_season .name } (ID: { new_season .id } )" )
954+ return new_season
955+
956+ except Exception as e :
957+ _logger .error (f"Failed to generate/find season: { str (e )} " )
958+ raise ValidationError (_ (f"Failed to create or find agricultural season: { str (e )} " )) from e
850959
851960 def _get_random_species (self , species_type ):
852961 """Get a random species of the specified type"""
@@ -880,8 +989,21 @@ def _get_random_feed_items(self, limit=3):
880989 return [(6 , 0 , [])]
881990
882991 def _get_random_season (self ):
883- """Get a random active season"""
884- seasons = self .env ["spp.farm.season" ].search ([("state" , "=" , "active" )])
885- if seasons :
886- return random .choice (seasons ).id
992+ """Get the configured active season, creating it if necessary"""
993+ try :
994+ # Get faker locale safely
995+ faker_locale = "en_US"
996+ if hasattr (self , "locale_origin" ) and self .locale_origin :
997+ faker_locale = self .locale_origin .faker_locale or "en_US"
998+
999+ fake = Faker (faker_locale )
1000+ season = self ._generate_season_data (fake )
1001+
1002+ if season :
1003+ _logger .debug (f"Using season: { season .name } (ID: { season .id } )" )
1004+ return season .id
1005+ except Exception as e :
1006+ _logger .error (f"Exception while getting season: { str (e )} " , exc_info = True )
1007+
1008+ _logger .error ("Failed to generate/find active season! Agricultural activities cannot be created." )
8871009 return None
0 commit comments