Update monitoring PF competitor tracking
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var ErrNotFound = errors.New("not found")
|
||||
@@ -386,53 +387,34 @@ func (a *App) DeleteProject(ctx context.Context, ownerID, projectID int64) error
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
type DeletedListing struct {
|
||||
Listing *Listing
|
||||
ProjectTitle string
|
||||
ProjectDeal string
|
||||
OwnerChatID *string
|
||||
}
|
||||
|
||||
func (a *App) DeleteListing(ctx context.Context, ownerID, listingID int64) (*DeletedListing, error) {
|
||||
row := a.DB.QueryRowContext(ctx, listingSelect()+`
|
||||
JOIN projects p ON p.id = l.project_id
|
||||
WHERE l.id = ? AND p.owner_id = ?`, listingID, ownerID)
|
||||
listing, err := scanListing(row, false)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deleted := &DeletedListing{Listing: listing}
|
||||
var deal string
|
||||
var chat sql.NullString
|
||||
if err := a.DB.QueryRowContext(ctx, `
|
||||
SELECT p.title, p.deal_type, e.tg_chat_id
|
||||
FROM projects p
|
||||
JOIN employees e ON e.id = p.owner_id
|
||||
WHERE p.id = ? AND p.owner_id = ?`, listing.ProjectID, ownerID).
|
||||
Scan(&deleted.ProjectTitle, &deal, &chat); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deleted.ProjectDeal = enumDealOut(deal)
|
||||
deleted.OwnerChatID = nullableString(chat)
|
||||
|
||||
func (a *App) DeleteListing(ctx context.Context, ownerID, listingID int64) error {
|
||||
tx, err := a.DB.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM price_history WHERE listing_id = ?`, listing.ID); err != nil {
|
||||
return nil, err
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
DELETE FROM price_history
|
||||
WHERE listing_id IN (
|
||||
SELECT l.id
|
||||
FROM competitor_listings l
|
||||
JOIN projects p ON p.id = l.project_id
|
||||
WHERE l.id = ? AND p.owner_id = ?
|
||||
)`, listingID, ownerID); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM competitor_listings WHERE id = ?`, listing.ID); err != nil {
|
||||
return nil, err
|
||||
res, err := tx.ExecContext(ctx, `
|
||||
DELETE FROM competitor_listings
|
||||
WHERE id = ?
|
||||
AND project_id IN (SELECT id FROM projects WHERE owner_id = ?)`, listingID, ownerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
affected, _ := res.RowsAffected()
|
||||
if affected == 0 {
|
||||
return ErrNotFound
|
||||
}
|
||||
return deleted, nil
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (a *App) ListingByID(ctx context.Context, id int64, withHistory bool) (*Listing, error) {
|
||||
@@ -542,6 +524,87 @@ func (a *App) ListingsForProject(ctx context.Context, projectID int64, withHisto
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (a *App) TeamOverview(ctx context.Context, portalUserIDs []string, all bool) ([]TeamOverviewRow, error) {
|
||||
args := []any{}
|
||||
where := ""
|
||||
if !all {
|
||||
if len(portalUserIDs) == 0 {
|
||||
return []TeamOverviewRow{}, nil
|
||||
}
|
||||
placeholders := make([]string, 0, len(portalUserIDs))
|
||||
for _, id := range portalUserIDs {
|
||||
placeholders = append(placeholders, "?")
|
||||
args = append(args, id)
|
||||
}
|
||||
where = "WHERE e.portal_user_id IN (" + strings.Join(placeholders, ",") + ")"
|
||||
}
|
||||
rows, err := a.DB.QueryContext(ctx, `
|
||||
SELECT e.id,
|
||||
e.name,
|
||||
e.portal_user_id,
|
||||
e.tg_chat_id,
|
||||
p.id,
|
||||
p.title,
|
||||
p.deal_type,
|
||||
p.dld_permit,
|
||||
p.last_checked_at,
|
||||
count(l.id),
|
||||
sum(CASE WHEN l.status IN ('ACTIVE','active') THEN 1 ELSE 0 END),
|
||||
sum(CASE WHEN l.status IN ('REMOVED','removed') THEN 1 ELSE 0 END),
|
||||
min(CASE WHEN l.status IN ('ACTIVE','active') THEN l.current_price ELSE NULL END)
|
||||
FROM employees e
|
||||
LEFT JOIN projects p ON p.owner_id = e.id
|
||||
LEFT JOIN competitor_listings l ON l.project_id = p.id
|
||||
`+where+`
|
||||
GROUP BY e.id, e.name, e.portal_user_id, e.tg_chat_id,
|
||||
p.id, p.title, p.deal_type, p.dld_permit, p.last_checked_at, p.created_at
|
||||
ORDER BY e.name COLLATE NOCASE, p.created_at DESC`, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []TeamOverviewRow{}
|
||||
for rows.Next() {
|
||||
var item TeamOverviewRow
|
||||
var portalID, chatID, title, deal, permit, checked sql.NullString
|
||||
var projectID, total, active, removed sql.NullInt64
|
||||
var minPrice sql.NullFloat64
|
||||
if err := rows.Scan(
|
||||
&item.EmployeeID,
|
||||
&item.EmployeeName,
|
||||
&portalID,
|
||||
&chatID,
|
||||
&projectID,
|
||||
&title,
|
||||
&deal,
|
||||
&permit,
|
||||
&checked,
|
||||
&total,
|
||||
&active,
|
||||
&removed,
|
||||
&minPrice,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.PortalUserID = nullableString(portalID)
|
||||
item.TelegramLinked = chatID.Valid && chatID.String != ""
|
||||
item.ProjectID = nullableInt(projectID)
|
||||
item.ProjectTitle = nullableString(title)
|
||||
if deal.Valid {
|
||||
value := enumDealOut(deal.String)
|
||||
item.DealType = &value
|
||||
}
|
||||
item.DLDPermit = nullableString(permit)
|
||||
item.LastCheckedAt = timeOut(checked)
|
||||
item.ListingsTotal = nullIntValue(total)
|
||||
item.ListingsActive = nullIntValue(active)
|
||||
item.ListingsRemoved = nullIntValue(removed)
|
||||
item.MinCompetitorPrice = nullableFloat(minPrice)
|
||||
items = append(items, item)
|
||||
}
|
||||
return items, rows.Err()
|
||||
}
|
||||
|
||||
func (a *App) PriceHistory(ctx context.Context, listingID int64) ([]PricePoint, error) {
|
||||
rows, err := a.DB.QueryContext(ctx, `
|
||||
SELECT id, price, recorded_at
|
||||
|
||||
Reference in New Issue
Block a user