| | |
| | | task.MonthStats() |
| | | util.ResponseFormat(c, code.Success, nil) |
| | | } |
| | | |
| | | // WarehouseMonthStats |
| | | // @Tags 报表 |
| | | // @Summary 仓库月度统计库存报表 |
| | | // @Produce application/json |
| | | // @Param object body request.GetMonthStats true "查询参数" |
| | | // @Param Authorization header string true "token" |
| | | // @Success 200 {object} util.ResponseList{data=[]models.MonthStats} "成功" |
| | | // @Router /api-wms/v1/forms/warehouseMonthStats [post] |
| | | func (slf ReportFormsController) WarehouseMonthStats(c *gin.Context) { |
| | | var params request.GetMonthStats |
| | | if err := c.BindJSON(¶ms); err != nil { |
| | | util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误") |
| | | return |
| | | } |
| | | |
| | | if params.WarehouseID == 0 { |
| | | util.ResponseFormat(c, code.RequestParamError, "仓库ID参数缺失") |
| | | return |
| | | } |
| | | |
| | | monthFormsService := service.NewWarehouseMonthFormsService() |
| | | total, err := monthFormsService.Count(params) |
| | | if err != nil { |
| | | logx.Errorf("MonthStats count err:%v", err) |
| | | util.ResponseFormat(c, code.InternalError, "查询总数失败") |
| | | return |
| | | } |
| | | |
| | | result, err := monthFormsService.Query(params) |
| | | if err != nil { |
| | | logx.Errorf("MonthStats query err:%v", err) |
| | | util.ResponseFormat(c, code.InternalError, "查询失败") |
| | | return |
| | | } |
| | | |
| | | now := time.Now().Local() |
| | | today := now.Day() |
| | | nowMonth := now.Format("2006-01") |
| | | |
| | | day, dateStr, _ := service.NewSystemConfigService().GetInventoryCutOffPoint() |
| | | if nowMonth == params.Date && today < day || today == day && now.Format("15:04") < dateStr { //本月未至结算时间点 |
| | | productIds := make([]string, 0, len(result)) |
| | | for _, item := range result { |
| | | productIds = append(productIds, item.ProductId) |
| | | } |
| | | statsRecords, err := service.GetCurrentWarehouseStats(params.Date, params.WarehouseID, productIds) |
| | | if err != nil { |
| | | util.ResponseFormat(c, code.InternalError, "内部错误") |
| | | return |
| | | } |
| | | statsMap := models.WarehouseMonthStatsMap(statsRecords) |
| | | for k, v := range result { |
| | | if statsMap[v.ProductId] == nil { |
| | | continue |
| | | } |
| | | |
| | | result[k].OutputAmount = statsMap[v.ProductId].OutputAmount |
| | | result[k].EndAmount = statsMap[v.ProductId].EndAmount |
| | | result[k].InputAmount = statsMap[v.ProductId].InputAmount |
| | | result[k].InputItems = statsMap[v.ProductId].InputItems |
| | | result[k].OutputItems = statsMap[v.ProductId].OutputItems |
| | | } |
| | | |
| | | } |
| | | |
| | | util.ResponseFormatList(c, code.Success, result, int(total)) |
| | | } |
| | |
| | | ) |
| | | |
| | | type ( |
| | | // MonthStats 移动历史 |
| | | // MonthStats 月度统计 |
| | | MonthStats struct { |
| | | WmsModel |
| | | Id int `json:"id" gorm:"column:id;primary_key;AUTO_INCREMENT"` |
| | |
| | | return slf |
| | | } |
| | | |
| | | func (slf *OperationSearch) SetWarehouseId(warehouseId int) *OperationSearch { |
| | | slf.WarehouseId = warehouseId |
| | | return slf |
| | | } |
| | | |
| | | func (slf *OperationSearch) build() *gorm.DB { |
| | | var db = slf.Orm.Model(&Operation{}) |
| | | |
| | |
| | | db = db.Where("inventory_dealer_type in (?)", slf.InventoryDealerTypeIds) |
| | | } |
| | | |
| | | if slf.WarehouseId != 0 { |
| | | db = db.Where("warehouse_id = ?", slf.WarehouseId) |
| | | } |
| | | |
| | | return db |
| | | } |
| | | |
| | |
| | | AuxiliaryUnit string `json:"auxiliaryUnit" gorm:"type:varchar(191);comment:辅助单位"` |
| | | Remark string `gorm:"type:varchar(1024);comment:备注" json:"remark"` |
| | | IsInternalOutput bool `json:"isInternalOutput"` //是否调拨产生的出库 |
| | | DealerType string `json:"dealerType"` //出入库类型 |
| | | |
| | | Cost decimal.Decimal `json:"cost" ` //成本单价 |
| | | SalePrice decimal.Decimal `json:"salePrice" ` //销售单价 |
| | |
| | | } |
| | | return result, nil |
| | | } |
| | | |
| | | type GroupByDealerTypeWarehouse struct { |
| | | DealerType string |
| | | ProductID string |
| | | Sum decimal.Decimal |
| | | } |
| | | |
| | | func (slf *OperationDetailsSearch) GroupMultiSumAmount() ([]*GroupByDealerTypeWarehouse, error) { |
| | | var ( |
| | | db = slf.build() |
| | | result = make([]*GroupByDealerTypeWarehouse, 0) |
| | | ) |
| | | if err := db.Select("sum(amount) as sum, dealer_type, product_id").Group("product_id, dealer_type").Scan(&result).Error; err != nil { |
| | | return nil, fmt.Errorf("select group err: %v", err) |
| | | } |
| | | return result, nil |
| | | } |
New file |
| | |
| | | package models |
| | | |
| | | import ( |
| | | "fmt" |
| | | "github.com/shopspring/decimal" |
| | | "gorm.io/gorm" |
| | | "wms/pkg/mysqlx" |
| | | ) |
| | | |
| | | type ( |
| | | // WarehouseMonthStats 按仓库进行月度统计 |
| | | WarehouseMonthStats struct { |
| | | WmsModel |
| | | Id int `json:"id" gorm:"column:id;primary_key;AUTO_INCREMENT"` |
| | | WarehouseId int `json:"warehouseId" gorm:"type:int;not null;default:0"` //仓库ID |
| | | ProductId string `json:"productId" gorm:"type:varchar(255);not null;comment:产品id"` //产品id |
| | | ProductName string `json:"productName" gorm:"type:varchar(255);not null;comment:产品名称"` //产品名称 |
| | | Unit string `json:"unit" gorm:"type:char(10);not null;comment:单位"` //单位 |
| | | SalePrice decimal.Decimal `gorm:"type:decimal(35,18);comment:销售单价" json:"salePrice"` //销售单价 |
| | | |
| | | BeginAmount decimal.Decimal `json:"beginAmount" gorm:"type:decimal(30,10);not null;comment:数量"` //期初数量 |
| | | EndAmount decimal.Decimal `json:"amount" gorm:"type:decimal(30,10);not null;comment:数量"` //期末结余数量 |
| | | |
| | | InputAmount decimal.Decimal `json:"inputAmount" gorm:"type:decimal(30,10);not null;comment:数量"` //入库数量 |
| | | InputItems []*WarehouseStatsItems `json:"inputMoreUnitsArr"` //入库明细 |
| | | |
| | | OutputAmount decimal.Decimal `json:"outputAmount" gorm:"type:decimal(30,10);not null;comment:数量"` //出库数量 |
| | | OutputItems []*WarehouseStatsItems `json:"outputMoreUnitsArr"` //出库明细 |
| | | |
| | | Date string `json:"date" gorm:"index;type:varchar(255); not null;default ''"` //日期 2024-04 |
| | | } |
| | | |
| | | WarehouseStatsItems struct { |
| | | WarehouseMonthStatsId int `json:"warehouseMonthStatsId"` |
| | | Name string `json:"name" gorm:"type:varchar(255);not null;default:''"` //入库来源,出库去处 |
| | | Amount decimal.Decimal `json:"amount" gorm:"type:decimal(30,10);not null;"` //数量 |
| | | } |
| | | |
| | | WarehouseMonthStatsSearch struct { |
| | | WarehouseMonthStats |
| | | Order string |
| | | PageNum int |
| | | PageSize int |
| | | Keyword string |
| | | Orm *gorm.DB |
| | | Preload bool |
| | | Fields string |
| | | } |
| | | ) |
| | | |
| | | func (slf *WarehouseMonthStats) TableName() string { |
| | | return "wms_month_stats" |
| | | } |
| | | |
| | | func NewWarehouseMonthStatsSearch() *WarehouseMonthStatsSearch { |
| | | return &WarehouseMonthStatsSearch{Orm: mysqlx.GetDB()} |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) SetOrm(tx *gorm.DB) *WarehouseMonthStatsSearch { |
| | | slf.Orm = tx |
| | | return slf |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) SetPage(page, size int) *WarehouseMonthStatsSearch { |
| | | slf.PageNum, slf.PageSize = page, size |
| | | return slf |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) SetOrder(order string) *WarehouseMonthStatsSearch { |
| | | slf.Order = order |
| | | return slf |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) SetID(id int) *WarehouseMonthStatsSearch { |
| | | slf.Id = id |
| | | return slf |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) SetKeyword(keyword string) *WarehouseMonthStatsSearch { |
| | | slf.Keyword = keyword |
| | | return slf |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) SetPreload(preload bool) *WarehouseMonthStatsSearch { |
| | | slf.Preload = preload |
| | | return slf |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) SetDate(date string) *WarehouseMonthStatsSearch { |
| | | slf.Date = date |
| | | return slf |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) SetFields(fields string) *WarehouseMonthStatsSearch { |
| | | slf.Fields = fields |
| | | return slf |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) build() *gorm.DB { |
| | | var db = slf.Orm.Model(&WarehouseMonthStats{}) |
| | | |
| | | if slf.Id != 0 { |
| | | db = db.Where("id = ?", slf.Id) |
| | | } |
| | | |
| | | if slf.Order != "" { |
| | | db = db.Order(slf.Order) |
| | | } |
| | | |
| | | if slf.Keyword != "" { |
| | | kw := fmt.Sprintf("%%%v%%", slf.Keyword) |
| | | db = db.Where("product_id like ? or product_name like ?", kw, kw) |
| | | } |
| | | |
| | | if slf.Date != "" { |
| | | db = db.Where("date = ?", slf.Date) |
| | | } |
| | | |
| | | if slf.Fields != "" { |
| | | db = db.Select(slf.Fields) |
| | | } |
| | | |
| | | return db |
| | | } |
| | | |
| | | // Create 单条插入 |
| | | func (slf *WarehouseMonthStatsSearch) Create(record *WarehouseMonthStats) error { |
| | | var db = slf.build() |
| | | |
| | | if err := db.Create(record).Error; err != nil { |
| | | return err |
| | | } |
| | | |
| | | return nil |
| | | } |
| | | |
| | | // CreateBatch 批量插入 |
| | | func (slf *WarehouseMonthStatsSearch) CreateBatch(records []*WarehouseMonthStats) error { |
| | | var db = slf.build() |
| | | |
| | | if err := db.Create(&records).Error; err != nil { |
| | | return fmt.Errorf("create batch err: %v, records: %+v", err, records) |
| | | } |
| | | |
| | | return nil |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) Update(record *WarehouseMonthStats) error { |
| | | var db = slf.build() |
| | | |
| | | if err := db.Omit("CreatedAt").Updates(record).Error; err != nil { |
| | | return fmt.Errorf("save err: %v, record: %+v", err, record) |
| | | } |
| | | |
| | | return nil |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) UpdateByMap(upMap map[string]interface{}) error { |
| | | var ( |
| | | db = slf.build() |
| | | ) |
| | | |
| | | if err := db.Updates(upMap).Error; err != nil { |
| | | return fmt.Errorf("update by map err: %v, upMap: %+v", err, upMap) |
| | | } |
| | | |
| | | return nil |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) UpdateByQuery(query string, args []interface{}, upMap map[string]interface{}) error { |
| | | var ( |
| | | db = slf.Orm.Table(slf.TableName()).Where(query, args...) |
| | | ) |
| | | |
| | | if err := db.Updates(upMap).Error; err != nil { |
| | | return fmt.Errorf("update by query err: %v, query: %s, args: %+v, upMap: %+v", err, query, args, upMap) |
| | | } |
| | | |
| | | return nil |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) Delete() error { |
| | | var db = slf.build() |
| | | return db.Delete(&WarehouseMonthStats{}).Error |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) First() (*WarehouseMonthStats, error) { |
| | | var ( |
| | | record = new(WarehouseMonthStats) |
| | | db = slf.build() |
| | | ) |
| | | |
| | | if err := db.First(record).Error; err != nil { |
| | | return record, err |
| | | } |
| | | |
| | | return record, nil |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) Find() ([]*WarehouseMonthStats, int64, error) { |
| | | var ( |
| | | records = make([]*WarehouseMonthStats, 0) |
| | | total int64 |
| | | db = slf.build() |
| | | ) |
| | | |
| | | if err := db.Count(&total).Error; err != nil { |
| | | return records, total, fmt.Errorf("find count err: %v", err) |
| | | } |
| | | if slf.PageNum*slf.PageSize > 0 { |
| | | db = db.Offset((slf.PageNum - 1) * slf.PageSize).Limit(slf.PageSize) |
| | | } |
| | | if err := db.Find(&records).Error; err != nil { |
| | | return records, total, fmt.Errorf("find records err: %v", err) |
| | | } |
| | | |
| | | return records, total, nil |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) FindNotTotal() ([]*WarehouseMonthStats, error) { |
| | | var ( |
| | | records = make([]*WarehouseMonthStats, 0) |
| | | db = slf.build() |
| | | ) |
| | | |
| | | if slf.PageNum*slf.PageSize > 0 { |
| | | db = db.Offset((slf.PageNum - 1) * slf.PageSize).Limit(slf.PageSize) |
| | | } |
| | | if err := db.Find(&records).Error; err != nil { |
| | | return records, fmt.Errorf("find records err: %v", err) |
| | | } |
| | | |
| | | return records, nil |
| | | } |
| | | |
| | | // FindByQuery 指定条件查询. |
| | | func (slf *WarehouseMonthStatsSearch) FindByQuery(query string, args []interface{}) ([]*WarehouseMonthStats, int64, error) { |
| | | var ( |
| | | records = make([]*WarehouseMonthStats, 0) |
| | | total int64 |
| | | db = slf.Orm.Table(slf.TableName()).Where(query, args...) |
| | | ) |
| | | |
| | | if err := db.Count(&total).Error; err != nil { |
| | | return records, total, fmt.Errorf("find by query count err: %v", err) |
| | | } |
| | | if slf.PageNum*slf.PageSize > 0 { |
| | | db = db.Offset((slf.PageNum - 1) * slf.PageSize).Limit(slf.PageSize) |
| | | } |
| | | if err := db.Find(&records).Error; err != nil { |
| | | return records, total, fmt.Errorf("find by query records err: %v, query: %s, args: %+v", err, query, args) |
| | | } |
| | | |
| | | return records, total, nil |
| | | } |
| | | |
| | | // FindByQueryNotTotal 指定条件查询&不查询总条数. |
| | | func (slf *WarehouseMonthStatsSearch) FindByQueryNotTotal(query string, args []interface{}) ([]*WarehouseMonthStats, error) { |
| | | var ( |
| | | records = make([]*WarehouseMonthStats, 0) |
| | | db = slf.Orm.Table(slf.TableName()).Where(query, args...) |
| | | ) |
| | | |
| | | if slf.PageNum*slf.PageSize > 0 { |
| | | db = db.Offset((slf.PageNum - 1) * slf.PageSize).Limit(slf.PageSize) |
| | | } |
| | | if err := db.Find(&records).Error; err != nil { |
| | | return records, fmt.Errorf("find by query records err: %v, query: %s, args: %+v", err, query, args) |
| | | } |
| | | |
| | | return records, nil |
| | | } |
| | | |
| | | func WarehouseMonthStatsMap(records []*WarehouseMonthStats) (m map[string]*WarehouseMonthStats) { |
| | | m = make(map[string]*WarehouseMonthStats, len(records)) |
| | | for _, record := range records { |
| | | m[record.ProductId] = record |
| | | } |
| | | return m |
| | | } |
| | | |
| | | func (slf *WarehouseMonthStatsSearch) Count() (int64, error) { |
| | | var ( |
| | | total int64 |
| | | db = slf.build() |
| | | ) |
| | | err := db.Count(&total).Error |
| | | return total, err |
| | | } |
| | |
| | | |
| | | type GetMonthStats struct { |
| | | PageInfo |
| | | Keyword string `json:"keyword"` |
| | | Date string `json:"date"` |
| | | Keyword string `json:"keyword"` |
| | | Date string `json:"date"` |
| | | WarehouseID int `json:"warehouseID"` |
| | | } |
| | | |
| | | type DoMonthStats struct { |
| | |
| | | reportFormsAPI.POST("monthStats", reportFormsController.MonthStats) //获取月度统计报表 |
| | | reportFormsAPI.POST("downloadMonthStats", reportFormsController.DownloadMonthStats) //下载月度统计报表 |
| | | reportFormsAPI.POST("doMonthStats", reportFormsController.DoMonthStats) //手动跑月度统计库存报表 |
| | | |
| | | reportFormsAPI.POST("warehouseMonthStats", reportFormsController.WarehouseMonthStats) //按仓库获取月度统计报表 |
| | | } |
| | | |
| | | //重订货规则 |
New file |
| | |
| | | package service |
| | | |
| | | import ( |
| | | "github.com/shopspring/decimal" |
| | | "time" |
| | | "wms/constvar" |
| | | "wms/models" |
| | | "wms/pkg/logx" |
| | | "wms/request" |
| | | "wms/utils" |
| | | ) |
| | | |
| | | type WarehouseMonthFormsService struct{} |
| | | |
| | | func NewWarehouseMonthFormsService() *WarehouseMonthFormsService { |
| | | return &WarehouseMonthFormsService{} |
| | | } |
| | | |
| | | func (slf *WarehouseMonthFormsService) Query(params request.GetMonthStats) (result []*models.WarehouseMonthStats, err error) { |
| | | search := slf.BuildSearch(params) |
| | | search = search.SetPage(params.Page, params.PageSize) |
| | | return search.FindNotTotal() |
| | | } |
| | | |
| | | func (slf *WarehouseMonthFormsService) BuildSearch(params request.GetMonthStats) (search *models.WarehouseMonthStatsSearch) { |
| | | search = models.NewWarehouseMonthStatsSearch().SetKeyword(params.Keyword).SetDate(params.Date) |
| | | return search |
| | | } |
| | | |
| | | func (slf *WarehouseMonthFormsService) Count(params request.GetMonthStats) (total int64, err error) { |
| | | search := slf.BuildSearch(params) |
| | | return search.Count() |
| | | } |
| | | |
| | | func (slf *WarehouseMonthFormsService) FetchAll(params request.GetMonthStats) (list []*models.WarehouseMonthStats, err error) { |
| | | total, err := slf.Count(params) |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | | list = make([]*models.WarehouseMonthStats, 0, total) |
| | | params.PageSize = 500 |
| | | page := 1 |
| | | for { |
| | | params.Page = page |
| | | data, err := slf.Query(params) |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | | if len(data) == 0 { |
| | | break |
| | | } |
| | | list = append(list, data...) |
| | | page++ |
| | | } |
| | | return |
| | | } |
| | | |
| | | func GetCurrentWarehouseStats(date string, warehouseId int, productIds []string) (statRecords []*models.WarehouseMonthStats, err error) { |
| | | //本月期初数量/上月结余数量 |
| | | groupSumList, err := models.NewLocationProductAmountSearch().SetProductIds(productIds).GroupSum("product_id", "amount") |
| | | productIds = make([]string, 0, len(groupSumList)) |
| | | for _, groupSum := range groupSumList { |
| | | productIds = append(productIds, groupSum.Class) |
| | | } |
| | | products, err := models.NewMaterialSearch().SetFields("id, name, unit, weight, more_unit, more_unit_value").SetIDs(productIds).FindNotTotal() |
| | | if err != nil { |
| | | logx.Errorf("MonthStats GetCurrentStats get products err:%v", err) |
| | | return |
| | | } |
| | | productMap := models.MaterialMap(products) |
| | | |
| | | beginTime, endTime := utils.GetLastMonthPeriod() |
| | | inputMap, err := GetStatsMulti(beginTime, endTime, constvar.BaseOperationTypeIncoming, warehouseId) |
| | | if err != nil { |
| | | logx.Errorf("MonthStats GetStatsByOperationType input err:%v", err) |
| | | return |
| | | } |
| | | |
| | | outputMap, err := GetStatsMulti(beginTime, endTime, constvar.BaseOperationTypeOutgoing, warehouseId) |
| | | if err != nil { |
| | | logx.Errorf("MonthStats GetStatsByOperationType output err:%v", err) |
| | | return |
| | | } |
| | | |
| | | for _, groupSum := range groupSumList { |
| | | productId := groupSum.Class |
| | | if productMap[productId] == nil { |
| | | continue |
| | | } |
| | | product := productMap[productId] |
| | | |
| | | amount := groupSum.Sum |
| | | record := models.WarehouseMonthStats{ |
| | | WarehouseId: warehouseId, |
| | | ProductId: productId, |
| | | ProductName: product.Name, |
| | | Unit: product.Unit, |
| | | SalePrice: product.SalePrice, |
| | | EndAmount: amount, |
| | | InputAmount: SumMapAmount(inputMap[productId]), |
| | | InputItems: GetDealerItems(inputMap[productId]), |
| | | OutputAmount: SumMapAmount(outputMap[productId]), |
| | | OutputItems: GetDealerItems(outputMap[productId]), |
| | | Date: date, |
| | | } |
| | | statRecords = append(statRecords, &record) |
| | | } |
| | | |
| | | return |
| | | } |
| | | |
| | | func GetStatsMulti(beginTime, endTime time.Time, operationType constvar.BaseOperationType, warehouseId int) (m map[string]map[string]decimal.Decimal, err error) { |
| | | operationIds, err := models.NewOperationSearch().SetBaseOperationType(operationType). |
| | | SetFields("id").SetTimeBetween(beginTime, endTime). |
| | | SetWarehouseId(warehouseId). |
| | | FindIds() |
| | | if err != nil { |
| | | return |
| | | } |
| | | groupSumList, err := models.NewOperationDetailsSearch().SetOperationIds(operationIds). |
| | | SetFields("product_id, dealer_type, amount"). |
| | | GroupMultiSumAmount() |
| | | if err != nil { |
| | | return |
| | | } |
| | | m = make(map[string]map[string]decimal.Decimal) |
| | | for _, v := range groupSumList { |
| | | if m[v.ProductID] == nil { |
| | | m[v.ProductID] = make(map[string]decimal.Decimal) |
| | | } |
| | | m[v.ProductID][v.DealerType] = v.Sum |
| | | } |
| | | return |
| | | } |
| | | |
| | | func SumMapAmount(m map[string]decimal.Decimal) (sum decimal.Decimal) { |
| | | for _, v := range m { |
| | | sum = sum.Add(v) |
| | | } |
| | | return |
| | | } |
| | | |
| | | func GetDealerItems(m map[string]decimal.Decimal) (items []*models.WarehouseStatsItems) { |
| | | for k, v := range m { |
| | | items = append(items, &models.WarehouseStatsItems{ |
| | | Name: k, |
| | | Amount: v, |
| | | }) |
| | | } |
| | | return |
| | | } |